1. Introduction and Aims

Two datasets were generated using the 10X Genomics Chromium 3’ scRNA-Seq platform:

species experiment_name run_number lane_number sequencer approximate_number_of_cells
pb straight bleed experiment 22252 5 Hiseq 4000 30,000
pb 1:1 mix experiment 24284 1 & 2 Hiseq 2500 5,000

This data has been processed using CellRanger into counts tables. This initial analysis gave the following metrics:

Pb 1:1 mix experiment (run #: 24284 lanes 1 and 2 (Hiseq 2500)):

We will load this data in and for each run:

A. Define ‘cells’

B. Filter poor quality cells out

C. Dimensionality Reduction and Clustering

D. Remove Doublets

E. Predict life Cycle Stage (Using Bulk RNA-Seq Correlation)

2. Read in the data

Load the required packages

[1] "Seurat is loaded correctly"
[1] "cowplot is loaded correctly"
[1] "gridExtra is loaded correctly"
[1] "grid is loaded correctly"
[1] "Hmisc is loaded correctly"
[1] "dplyr is loaded correctly"
[1] "scales is loaded correctly"
[1] "ggpubr is loaded correctly"
[1] "patchwork is loaded correctly"

Import GTF file

This will be helpful later on. This contains annotations for each gene:

##Import gtf file:
gtf <- read.table("/Users/Andy/GCSKO/GCSKO_analysis_git/data/Reference/Pberghei.gtf", sep="\t", header = FALSE)
head(gtf)

Read in the Data

## read in 10x output 
tenx5k_raw_data <- Read10X("/Users/Andy/GCSKO/GCSKO_analysis_git/data/10X/tenx_24284")

## Create Seurat object
tenx5k <- CreateSeuratObject(counts = tenx5k_raw_data, min.cells = 0, min.features = 0, project = "GCSKO")
Feature names cannot have underscores ('_'), replacing with dashes ('-')
## add experiment to meta data for merging later
tenx5k@meta.data$experiment <- "tenx5k"

3. Defining Cells vs. Background

Plot a knee plot and then use a mixture model to define where the cells vs. background lie

## interesting reference material for this section can be found here: https://hemberg-lab.github.io/scRNA.seq.course/processing-raw-scrna-seq-data.html 

## get the nUMIs
umi_per_barcode <- as.data.frame(tenx5k@meta.data$nCount_RNA)

## remove zeros as these have issues when you log them and make the model later:
umi_per_barcode <- as.data.frame(umi_per_barcode[!(umi_per_barcode$`tenx5k@meta.data$nCount_RNA`==0), ])

## get a rank for each barcode
barcode_rank <- rank(-umi_per_barcode[,1])

## then make into a list
lib_size <- (umi_per_barcode[,1])

## then log this
log_lib_size <- log10(umi_per_barcode[,1])

##plot
#plot(barcode_rank, log_lib_size, xlim=c(1,100000))

## order the barcode ranks
o <- order(barcode_rank)

## reorder the library size, barcode rank by their rank
log_lib_size <- log_lib_size[o]
barcode_rank <- barcode_rank[o]
lib_size <- lib_size[o]

make a mixture model to determine the knee of the plot

## set a seed for the mixture model
set.seed(-92497)

## mixture model calculation 
require("mixtools")
mix <- normalmixEM(log_lib_size)
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
number of iterations= 17 
## plot result
plot(mix, which=2, xlab2="log(mol per cell)")

Find where the distributions intersect (i.e. where cells vs. background is)

## identify where the split between the distributions is
p1 <- dnorm(log_lib_size, mean=mix$mu[1], sd=mix$sigma[1])
p2 <- dnorm(log_lib_size, mean=mix$mu[2], sd=mix$sigma[2])
if (mix$mu[1] < mix$mu[2]) {
    split <- min(log_lib_size[p2 > p1])
} else {
    split <- min(log_lib_size[p1 > p2])
}

## print split
split
[1] 1.724276

View the initial result

## log the barcode rank
log_barcode_rank <- log10(barcode_rank)

## plot
plot(log_barcode_rank, log_lib_size, xlim=c(1,6))
## add the split as a line on the plot
abline(h=split, col="red")

Final Figures:

## make the results of the above functions into a dataframe
df_barcodes <- as.data.frame(cbind(barcode_rank, log_lib_size, lib_size), row.names = NULL)

## add a column for if it is a cell or not
df_barcodes$cell = rownames(df_barcodes) %in% which(df_barcodes$log_lib_size > split)

## change value to a numeric
df_barcodes$cell <- as.numeric(df_barcodes$cell)

## change the 0 to a 2 for ease of handling
df_barcodes$cell[df_barcodes$cell<1] <- 2

## rename the numerics into cells or background
df_barcodes$cell[df_barcodes$cell == 1] <- "Cells"
df_barcodes$cell[df_barcodes$cell == 2] <- "Background"

## extract the cutoff for cells do you can plot the lines
boundary <- as.numeric(sum(df_barcodes$cell == "Cells"))
split <- 10^split

## make the plot
barcode_plot <- ggplot(df_barcodes, aes(x=barcode_rank, y=lib_size, colour = cell, theme_size = 40)) +
  ## make into a dot plot
  geom_point(size = 1, shape = 16) +
  ## make the axis into log and specify breaks
  scale_x_log10(breaks = trans_breaks("log10", function(x) 10^x), labels = trans_format("log10", math_format(10^.x))) +
  ## make the axis into log and specify breaks
  scale_y_log10(breaks = trans_breaks("log10", function(x) 10^x), labels = trans_format("log10", math_format(10^.x))) +
  annotation_logticks() +
  ## change colours of plot
  scale_color_manual(values=c("#bdbdbd", "#5ba43a"), labels = c("Background", "Cells")) +
  ## change aes of legend
  theme(legend.position="bottom", text = element_text(size=25), legend.text=element_text(size=25), axis.text=element_text(size=25)) +
  ## add the lines on the plot
  geom_segment(aes(x = 0, y = split, xend = boundary, yend = split), colour = "black", alpha = 0.01) +
  geom_segment(aes(x = boundary, y = 0, xend = boundary, yend = split), colour = "black", alpha = 0.01) +
  ## change the axis labels
  labs(x = "Barcodes", y = "UMI Counts", colour="Cell Designation") +
  ## change the size of the legend
  guides(colour = guide_legend(override.aes = list(size=10))) +
  ## fix axis
  coord_fixed() +
  ## make it look pretty
  theme_light()

## print the plot
barcode_plot

## save the plot
ggsave("barcode_plot_5k.png", plot = barcode_plot, device = "png", height = 15, width = 15, units = "cm", path = "/Users/Andy/GCSKO/GCSKO_analysis_git/images_to_export/")

so the number of cells that is retained is:


Background      Cells 
    138879       7762 

Filter the object

## extract the nCount and row names from the Seurat object
upb <- data.frame(nCount_RNA = tenx5k@meta.data$nCount_RNA, row.names = rownames(tenx5k@meta.data))

## make blank column for rank
upb$rank <- NA

## order by nCounts
order.scores <- order(upb$nCount_RNA, decreasing = TRUE)

## add a rank to this column based on the ordered nCount
upb$rank[order.scores] <- 1:nrow(upb)

## inspect
#head(upb)

## make a list of cells to retain in the Seurat object
keep_cells <- rownames(upb[which(upb$rank < 7763),])

## subset Seurat object to include cells and discard background
pb_sex <- subset(tenx5k, cells = keep_cells)

4. Filter Out Poor-Quality Cells

Filter Mitochondrial %

Mitochondrial cell counts

## extract mitochondrial genes 
#mito_genes <- gtf[which(gtf$V3 == "rRNA"),]$V9
#mito_genes <- gsub(";.*","", gsub("gene_id ", "", mito_genes))
#paste("These are the mitochondrial genes")
#head(mito_genes)

## extract mito genes
mito_genes <- pb_sex@assays$RNA@counts@Dimnames[[1]][grep("^PBANKA-MIT",pb_sex@assays$RNA@counts@Dimnames[[1]])]

## plot the genes individually
VlnPlot(object = pb_sex, features = mito_genes, pt.size = 0.01)
All cells have the same value of PBANKA-MIT0010.All cells have the same value of PBANKA-MIT0020.All cells have the same value of PBANKA-MIT0030.All cells have the same value of PBANKA-MIT0040.All cells have the same value of PBANKA-MIT0050.All cells have the same value of PBANKA-MIT0090.All cells have the same value of PBANKA-MIT0130.All cells have the same value of PBANKA-MIT0140.All cells have the same value of PBANKA-MIT0150.All cells have the same value of PBANKA-MIT0160.All cells have the same value of PBANKA-MIT0200.All cells have the same value of PBANKA-MIT0210.All cells have the same value of PBANKA-MIT0230.All cells have the same value of PBANKA-MIT0240.All cells have the same value of PBANKA-MIT0250.All cells have the same value of PBANKA-MIT0260.All cells have the same value of PBANKA-MIT0290.All cells have the same value of PBANKA-MIT0300.All cells have the same value of PBANKA-MIT0320.All cells have the same value of PBANKA-MIT0330.All cells have the same value of PBANKA-MIT0340.All cells have the same value of PBANKA-MIT0370.

## make a percentage mitocondrial for each cell (this will work as long as you filter cells out with zero counts)
pb_sex <- PercentageFeatureSet(pb_sex, pattern = "^PBANKA-MIT", col.name = "percent.mt")

plot percentage mitochondrial

## plot for percentage of mitochondrial reads
v1 <- VlnPlot(object = pb_sex, features = "percent.mt", pt.size = 0.01) +
  ## add a line where we will filter
  geom_abline(intercept = 20, col="blue") +
  ## change labels
  labs(x = "",y = "% Mitochondrial Reads", title = "Mitochondrial per cell") +
  ## remove legend
  theme(legend.position = "none") +
  ## change appearance
  theme_classic() +
  scale_fill_manual(values="grey") +
  #scale_y_continuous(limits = c(0, 100)) +
  theme(legend.position="none", axis.text.x = element_blank(), axis.ticks.x=element_blank(), text = element_text(size=20), legend.text=element_text(size=20), axis.text=element_text(size=20), axis.text.y=element_text(colour="black"))

## print
v1


## save
#ggsave("~/images_to_export/QC_10X_mito_violin.png", plot = QC_mito_violin, device = "png", path = NULL, scale = 1, width = 15, height = 10, units = "cm", dpi = 300, limitsize = TRUE)

In the Smart-seq2 data, we use a threshold of 20%. No cell in our 10X data is higher than 13% and only

[1] 10

cells have a % mitochondrial reads above 5%.

nGenes filter

plot individual violin plots

## nGenes plot
gene_plot_5k <- VlnPlot(object = pb_sex, features = "nFeature_RNA", pt.size = 0.01)

## improve the aesthetics
gene_plot_5k <- gene_plot_5k + 
  geom_abline(intercept = 200, col="blue") +
  labs(x = "",y = "nGene", title = "Genes per cell") +
  theme_classic() +
  scale_fill_manual(values="grey") +
  scale_y_continuous(limits = c(0, 3000)) +
  theme(legend.position="none", axis.text.x = element_blank(), axis.ticks.x=element_blank(), text = element_text(size=20), legend.text=element_text(size=20), axis.text=element_text(size=20), axis.text.y=element_text(colour="black"))
Scale for 'y' is already present. Adding another scale for 'y', which will replace the
existing scale.
## nUMI plot
numi_plot_5k <- VlnPlot(object = pb_sex, features = "nCount_RNA", pt.size = 0.01)

## improve aesthetics
numi_plot_5k <- numi_plot_5k +
  labs(x = "",y = "nUMI", title = "UMIs per cell") +
  theme_classic() +
  scale_fill_manual(values="grey") +
  scale_y_continuous(limits = c(0, 3000)) +
  theme(legend.position="none", axis.text.x = element_blank(), axis.ticks.x=element_blank(), text = element_text(size=20), legend.text=element_text(size=20), axis.text=element_text(size=20), axis.text.y=element_text(colour="black"))
Scale for 'y' is already present. Adding another scale for 'y', which will replace the
existing scale.
## plot together
grid.arrange(gene_plot_5k, numi_plot_5k, ncol = 2, top=textGrob("5K cells 10X", gp=gpar(fontsize=15,font=8)))


## save nGene plot on its own
#ggsave("ngene_plot.pdf", plot = gene1, device = "pdf", height = 5, width = 5, units = "in", path = "/Users/ar19/Desktop/PhD/GCSKO_Analysis")

plot two metrics together

## make a dataframe for important filtering metrics
df <- data.frame(nCount = log10(pb_sex@meta.data$nCount_RNA), nGenes = pb_sex@meta.data$nFeature_RNA, percent_mt = pb_sex@meta.data$percent.mt, experiment = pb_sex@meta.data$experiment)

## plot main dotplot
plot1 <- ggplot(df, aes(x = nCount, y = nGenes)) + 
  geom_point(aes(), size = 0.1) +
  geom_rug() + 
  scale_y_continuous(name = "Number of Detected Genes") + 
  scale_x_continuous(name = "log10(Number of Total UMI)") + 
  theme_pubr() +
  theme(legend.position = "bottom") +
  geom_hline(yintercept=200)

## plot density plot x
dens1 <- ggplot(df, aes(x = nCount)) + 
  geom_density(alpha = 0.2) + 
  theme_void() + 
  theme(legend.position = "none")

## plot density plot y
## old method
# dens2 <- ggplot(df, aes(x = nGenes, y = experiment)) +
#   geom_density(alpha = 0.2) +
#   theme_void() +
#   theme(legend.position = "none") +
#   coord_flip()
## new method
dens <- density(df$nGenes)
dt <- data.frame(x=dens$x, y=dens$y)
probs <- c(0, 0.25, 0.5, 0.75, 1)
quantiles <- quantile(df$nGenes, prob=probs)
dt$quant <- factor(findInterval(dt$x,quantiles))
dens2 <- ggplot(dt, aes(x,y)) + geom_line() + geom_ribbon(aes(ymin=0, ymax=y, fill=quant)) + scale_x_continuous(breaks=quantiles) + scale_fill_brewer(guide="none") + theme_void() + theme(legend.position = "none") +
  coord_flip()

## plot together
QC_composite_plot <- dens1 + plot_spacer() + plot1 + dens2 + plot_layout(ncol = 2, nrow = 2, widths = c(4, 1), heights = c(1, 4))

## print
QC_composite_plot


## save
ggsave("/Users/Andy/GCSKO/GCSKO_analysis_git/images_to_export/QC_10X_composite_plot.png", plot = QC_composite_plot, device = "png", path = NULL, scale = 1, width = 20, height = 20, units = "cm", dpi = 300, limitsize = TRUE)

Filtering

The threshold used in the malaria cell atlas was 230 for Pb but this is dependent on sequencing depth etc. We can plot the number of cells recovered for a range of thresholds:

paste("original number of cells =", nrow(pb_sex@meta.data))
[1] "original number of cells = 7762"
paste("with >150 filter =", nrow(pb_sex@meta.data[pb_sex@meta.data$nFeature_RNA > 150, ]))
[1] "with >150 filter = 7188"
paste("with >200 filter =", nrow(pb_sex@meta.data[pb_sex@meta.data$nFeature_RNA > 200, ]))
[1] "with >200 filter = 6631"
paste("with >230 filter =", nrow(pb_sex@meta.data[pb_sex@meta.data$nFeature_RNA > 230, ]))
[1] "with >230 filter = 6239"

Since we have already filtered on nUMI, we will filter with 200.

## number of cells before filtering
pb_sex_pre_filter_nCells <- nrow(pb_sex@meta.data)
## filter object
pb_sex <- subset(pb_sex, subset = nFeature_RNA > 200)
## number of cells after filtering
pb_sex_post_filter_nCells <- nrow(pb_sex@meta.data)
## print results of filtering
paste("number of cells pre-filter", pb_sex_pre_filter_nCells)
[1] "number of cells pre-filter 7762"
paste("number of cells post-filter", pb_sex_post_filter_nCells)
[1] "number of cells post-filter 6631"
paste((pb_sex_pre_filter_nCells - pb_sex_post_filter_nCells), "cells were removed by filtering on number of genes.")
[1] "1131 cells were removed by filtering on number of genes."

5. Dimensionality Reduction and Clustering

Prepare data

## normalise object
pb_sex <- NormalizeData(pb_sex, normalization.method = "LogNormalize", scale.factor = 10000)
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
## find variable genes
pb_sex <- FindVariableFeatures(pb_sex, selection.method = "vst", nfeatures = 2000)
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
## scale the data
all.genes <- rownames(pb_sex)
pb_sex <- ScaleData(pb_sex, features = all.genes)
Centering and scaling data matrix

  |                                                                                           
  |                                                                                     |   0%
  |                                                                                           
  |==============                                                                       |  17%
  |                                                                                           
  |============================                                                         |  33%
  |                                                                                           
  |==========================================                                           |  50%
  |                                                                                           
  |=========================================================                            |  67%
  |                                                                                           
  |=======================================================================              |  83%
  |                                                                                           
  |=====================================================================================| 100%

PCA

## run PCA
pb_sex <- RunPCA(pb_sex, features = VariableFeatures(object = pb_sex))
PC_ 1 
Positive:  PBANKA-1235600, PBANKA-0931300, PBANKA-1214300, PBANKA-1400400, PBANKA-1101100, PBANKA-0205000, PBANKA-0311800, PBANKA-0823400, PBANKA-1242300, PBANKA-1246600 
       PBANKA-0722600, PBANKA-1008500, PBANKA-1308600, PBANKA-0932400, PBANKA-1314800, PBANKA-1300096, PBANKA-1309900, PBANKA-1200096, PBANKA-1145400, PBANKA-0722801 
       PBANKA-1100441, PBANKA-1040521, PBANKA-1326400, PBANKA-1101300, PBANKA-0941300, PBANKA-1210200, PBANKA-1303800, PBANKA-0216061, PBANKA-0406500, PBANKA-1127000 
Negative:  PBANKA-1312700, PBANKA-1224900, PBANKA-0824400, PBANKA-1453000, PBANKA-1115200, PBANKA-1432200, PBANKA-1204200, PBANKA-0703500, PBANKA-0702000, PBANKA-0812600 
       PBANKA-0312700, PBANKA-1128800, PBANKA-1218300, PBANKA-1106500, PBANKA-1430600, PBANKA-1449300, PBANKA-0209300, PBANKA-1232600, PBANKA-1432400, PBANKA-1421500 
       PBANKA-1320800, PBANKA-0620400, PBANKA-1038800, PBANKA-1110700, PBANKA-1336200, PBANKA-0417600, PBANKA-1451100, PBANKA-1143800, PBANKA-1414500, PBANKA-0402700 
PC_ 2 
Positive:  PBANKA-1431500, PBANKA-1431400, PBANKA-0942400, PBANKA-1340400, PBANKA-0934700, PBANKA-1038700, PBANKA-0942300, PBANKA-1449000, PBANKA-1208800, PBANKA-0828900 
       PBANKA-0911000, PBANKA-0306100, PBANKA-0927700, PBANKA-0806800, PBANKA-0416100, PBANKA-0507800, PBANKA-1409600, PBANKA-0411300, PBANKA-0406200, PBANKA-0415800 
       PBANKA-1033700, PBANKA-1361500, PBANKA-0925400, PBANKA-0615200, PBANKA-1108800, PBANKA-0507300, PBANKA-0720100, PBANKA-0920700, PBANKA-1030400, PBANKA-0510600 
Negative:  PBANKA-1317200, PBANKA-1352500, PBANKA-0810700, PBANKA-1225500, PBANKA-1319500, PBANKA-0402500, PBANKA-0827400, PBANKA-1419300, PBANKA-0714000, PBANKA-1315300 
       PBANKA-0105100, PBANKA-1436300, PBANKA-1311400, PBANKA-0417600, PBANKA-0821400, PBANKA-0417200, PBANKA-0204500, PBANKA-1342300, PBANKA-0704900, PBANKA-1415200 
       PBANKA-0605800, PBANKA-1115000, PBANKA-0517600, PBANKA-1329900, PBANKA-1134900, PBANKA-1231300, PBANKA-1233600, PBANKA-1035200, PBANKA-0912500, PBANKA-1363600 
PC_ 3 
Positive:  PBANKA-1400600, PBANKA-1459300, PBANKA-0416000, PBANKA-0304800, PBANKA-0305100, PBANKA-1365200, PBANKA-1443300, PBANKA-0830200, PBANKA-0938400, PBANKA-0827200 
       PBANKA-1137800, PBANKA-1437300, PBANKA-0932200, PBANKA-0506100, PBANKA-1349000, PBANKA-0313800, PBANKA-1113300, PBANKA-0409800, PBANKA-1017100, PBANKA-1117000 
       PBANKA-0408500, PBANKA-1450300, PBANKA-0922500, PBANKA-0934300, PBANKA-0505200, PBANKA-0941800, PBANKA-0915200, PBANKA-0722921, PBANKA-0316600, PBANKA-0519400 
Negative:  PBANKA-1431500, PBANKA-1431400, PBANKA-1449000, PBANKA-0806800, PBANKA-0927700, PBANKA-0911000, PBANKA-1108000, PBANKA-0828900, PBANKA-0601200, PBANKA-1208800 
       PBANKA-1409600, PBANKA-0830900, PBANKA-0942400, PBANKA-1029400, PBANKA-1038700, PBANKA-0102700, PBANKA-1316500, PBANKA-1304500, PBANKA-0521200, PBANKA-1333100 
       PBANKA-0902400, PBANKA-1129600, PBANKA-1119800, PBANKA-0822900, PBANKA-1109600, PBANKA-0301800, PBANKA-1429100, PBANKA-0622000, PBANKA-1038200, PBANKA-0942300 
PC_ 4 
Positive:  PBANKA-0107300, PBANKA-1214300, PBANKA-0814200, PBANKA-0713300, PBANKA-0604300, PBANKA-0405200, PBANKA-0109100, PBANKA-0932200, PBANKA-0823400, PBANKA-1460400 
       PBANKA-1448000, PBANKA-0416500, PBANKA-0938400, PBANKA-0406500, PBANKA-1302800, PBANKA-1130500, PBANKA-1437300, PBANKA-1444100, PBANKA-0211500, PBANKA-1419100 
       PBANKA-0710100, PBANKA-1223100, PBANKA-1235600, PBANKA-1450300, PBANKA-0919600, PBANKA-0916200, PBANKA-1242300, PBANKA-1326400, PBANKA-0313800, PBANKA-0818900 
Negative:  PBANKA-0519000, PBANKA-0519100, PBANKA-0519300, PBANKA-0831000, PBANKA-1349100, PBANKA-1344400, PBANKA-1002400, PBANKA-0713100, PBANKA-0523700, PBANKA-0111000 
       PBANKA-0519400, PBANKA-1349000, PBANKA-1032100, PBANKA-1014500, PBANKA-0932000, PBANKA-0804500, PBANKA-1315700, PBANKA-0501400, PBANKA-1425900, PBANKA-0519200 
       PBANKA-1101400, PBANKA-0509600, PBANKA-0619700, PBANKA-1035400, PBANKA-0417800, PBANKA-1210600, PBANKA-1327100, PBANKA-0523800, PBANKA-0307500, PBANKA-1117200 
PC_ 5 
Positive:  PBANKA-0501600, PBANKA-1240600, PBANKA-1002600, PBANKA-0707700, PBANKA-0112200, PBANKA-1002400, PBANKA-0208900, PBANKA-0307500, PBANKA-0812200, PBANKA-1423000 
       PBANKA-0832800, PBANKA-1119600, PBANKA-1409200, PBANKA-0716900, PBANKA-0100400, PBANKA-1364400, PBANKA-1347000, PBANKA-0915000, PBANKA-0819600, PBANKA-1319000 
       PBANKA-1212500, PBANKA-1400091, PBANKA-0900900, PBANKA-1202000, PBANKA-1345900, PBANKA-0311500, PBANKA-1008600, PBANKA-0911700, PBANKA-1349100, PBANKA-0519200 
Negative:  PBANKA-1443300, PBANKA-0925600, PBANKA-0306700, PBANKA-1106500, PBANKA-0907200, PBANKA-0304800, PBANKA-1217400, PBANKA-1107600, PBANKA-1349000, PBANKA-1319300 
       PBANKA-1313100, PBANKA-0312500, PBANKA-0611700, PBANKA-1113300, PBANKA-0208000, PBANKA-1241800, PBANKA-0305100, PBANKA-1438000, PBANKA-1339300, PBANKA-0304400 
       PBANKA-0833300, PBANKA-0206300, PBANKA-0207500, PBANKA-0519400, PBANKA-0715300, PBANKA-1360000, PBANKA-0112100, PBANKA-1340500, PBANKA-0915200, PBANKA-1416800 
## plot 
DimPlot(pb_sex, reduction = "pca")


## elbow plot
ElbowPlot(pb_sex, ndims = 30, reduction = "pca")

UMAP

## run UMAP
pb_sex <- RunUMAP(pb_sex, dims = 1:10, seed.use = 1234, n.neighbors = 150)
16:05:48 UMAP embedding parameters a = 0.9922 b = 1.112
16:05:48 Read 6631 rows and found 10 numeric columns
16:05:48 Using Annoy for neighbor search, n_neighbors = 150
16:05:48 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
16:05:51 Writing NN index file to temp file /var/folders/wj/rztzclxn1t10cl2sk0plbf3r0000gn/T//RtmpyuC0hm/fileef95491096fc
16:05:51 Searching Annoy index using 1 thread, search_k = 15000
16:06:03 Annoy recall = 100%
16:06:05 Commencing smooth kNN distance calibration using 1 thread
16:06:09 Initializing from normalized Laplacian + noise
16:06:11 Commencing optimization for 500 epochs, with 1163176 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
16:06:31 Optimization finished
## plot
DimPlot(pb_sex, reduction = "umap", group.by = "ident", label = TRUE)


## These are the parameters used in the merge UMAP
#pb_sex <- RunUMAP(pb_sex, reduction = "pca", dims = 1:10, n.neighbors = 150, seed.use = 1234, min.dist = 0.4, repulsion.strength = 0.03, local.connectivity = 150)
#DimPlot(pb_sex, reduction = "umap", group.by = "ident", label = TRUE)

colour with a few marker genes:

# PBANKA-0515000 - p25 - female
# PBANKA-1212600 - HAP2 - male
# PBANKA-0600600 - NEK3 - male
# PBANKA-0831000 - MSP1 - late asexual
# PBANKA-1315700 - RON2 - (asexuals and some male?)
# PBANKA-0416100 - dynenin heavy chain - male - used in 820 line
# PBANKA-1319500 - CCP2 - female - used in 820 line 
# PBANKA-1437500 - AP2-G - seuxal commitment gene
# PBANKA-1102200 - MSP8 - early asexual (from Bozdech paper)

FeaturePlot(pb_sex, features = c("PBANKA-0515000", "PBANKA-1212600","PBANKA-0600600", "PBANKA-0831000", "PBANKA-1315700", "PBANKA-0416100", "PBANKA-1319500", "PBANKA-1437500", "PBANKA-1102200"))

Clustering

pb_sex <- FindNeighbors(pb_sex, dims = 1:21)
Computing nearest neighbor graph
Computing SNN
pb_sex <- FindClusters(pb_sex, resolution = 1)
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 6631
Number of edges: 277150

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8104
Number of communities: 14
Elapsed time: 1 seconds

6. Remove Doublets

DoubletFinder

DoubletFinder function

## DoubletFinder should be able to be installed and run as so:
#devtools::install_github('chris-mcginnis-ucsf/DoubletFinder')
#library(doubletFinder) #allows removal of doublets
## but there seems to be some problems with this so we will run it from the github code

## the doublet finder function
doubletFinder_v3 <- function(seu, PCs, pN = 0.25, pK, nExp, reuse.pANN = FALSE, sct = FALSE) {
  require(Seurat); require(fields); require(KernSmooth)

  ## Generate new list of doublet classificatons from existing pANN vector to save time
  if (reuse.pANN != FALSE ) {
    pANN.old <- seu@meta.data[ , reuse.pANN]
    classifications <- rep("Singlet", length(pANN.old))
    classifications[order(pANN.old, decreasing=TRUE)[1:nExp]] <- "Doublet"
    seu@meta.data[, paste("DF.classifications",pN,pK,nExp,sep="_")] <- classifications
    return(seu)
  }

  if (reuse.pANN == FALSE) {
    ## Make merged real-artifical data
    real.cells <- rownames(seu@meta.data)
    data <- seu@assays$RNA@counts[, real.cells]
    n_real.cells <- length(real.cells)
    n_doublets <- round(n_real.cells/(1 - pN) - n_real.cells)
    print(paste("Creating",n_doublets,"artificial doublets...",sep=" "))
    real.cells1 <- sample(real.cells, n_doublets, replace = TRUE)
    real.cells2 <- sample(real.cells, n_doublets, replace = TRUE)
    doublets <- (data[, real.cells1] + data[, real.cells2])/2
    colnames(doublets) <- paste("X", 1:n_doublets, sep = "")
    data_wdoublets <- cbind(data, doublets)

    ## Store important pre-processing information
    orig.commands <- seu@commands

    ## Pre-process Seurat object
    if (sct == FALSE) {
      print("Creating Seurat object...")
      seu_wdoublets <- CreateSeuratObject(counts = data_wdoublets)

      print("Normalizing Seurat object...")
      seu_wdoublets <- NormalizeData(seu_wdoublets,
                                     normalization.method = orig.commands$NormalizeData.RNA@params$normalization.method,
                                     scale.factor = orig.commands$NormalizeData.RNA@params$scale.factor,
                                     margin = orig.commands$NormalizeData.RNA@params$margin)

      print("Finding variable genes...")
      seu_wdoublets <- FindVariableFeatures(seu_wdoublets,
                                            selection.method = orig.commands$FindVariableFeatures.RNA$selection.method,
                                            loess.span = orig.commands$FindVariableFeatures.RNA$loess.span,
                                            clip.max = orig.commands$FindVariableFeatures.RNA$clip.max,
                                            mean.function = orig.commands$FindVariableFeatures.RNA$mean.function,
                                            dispersion.function = orig.commands$FindVariableFeatures.RNA$dispersion.function,
                                            num.bin = orig.commands$FindVariableFeatures.RNA$num.bin,
                                            binning.method = orig.commands$FindVariableFeatures.RNA$binning.method,
                                            nfeatures = orig.commands$FindVariableFeatures.RNA$nfeatures,
                                            mean.cutoff = orig.commands$FindVariableFeatures.RNA$mean.cutoff,
                                            dispersion.cutoff = orig.commands$FindVariableFeatures.RNA$dispersion.cutoff)

      print("Scaling data...")
      seu_wdoublets <- ScaleData(seu_wdoublets,
                                 features = orig.commands$ScaleData.RNA$features,
                                 model.use = orig.commands$ScaleData.RNA$model.use,
                                 do.scale = orig.commands$ScaleData.RNA$do.scale,
                                 do.center = orig.commands$ScaleData.RNA$do.center,
                                 scale.max = orig.commands$ScaleData.RNA$scale.max,
                                 block.size = orig.commands$ScaleData.RNA$block.size,
                                 min.cells.to.block = orig.commands$ScaleData.RNA$min.cells.to.block)

      print("Running PCA...")
      seu_wdoublets <- RunPCA(seu_wdoublets,
                              features = orig.commands$ScaleData.RNA$features,
                              npcs = length(PCs),
                              rev.pca =  orig.commands$RunPCA.RNA$rev.pca,
                              weight.by.var = orig.commands$RunPCA.RNA$weight.by.var,
                              verbose=FALSE)
      pca.coord <- seu_wdoublets@reductions$pca@cell.embeddings[ , PCs]
      cell.names <- rownames(seu_wdoublets@meta.data)
      nCells <- length(cell.names)
      rm(seu_wdoublets); gc() # Free up memory
    }

    if (sct == TRUE) {
      require(sctransform)
      print("Creating Seurat object...")
      seu_wdoublets <- CreateSeuratObject(counts = data_wdoublets)

      print("Running SCTransform...")
      seu_wdoublets <- SCTransform(seu_wdoublets)

      print("Running PCA...")
      seu_wdoublets <- RunPCA(seu_wdoublets, npcs = length(PCs))
      pca.coord <- seu_wdoublets@reductions$pca@cell.embeddings[ , PCs]
      cell.names <- rownames(seu_wdoublets@meta.data)
      nCells <- length(cell.names)
      rm(seu_wdoublets); gc()
    }

    ## Compute PC distance matrix
    print("Calculating PC distance matrix...")
    dist.mat <- fields::rdist(pca.coord)

    ## Compute pANN
    print("Computing pANN...")
    pANN <- as.data.frame(matrix(0L, nrow = n_real.cells, ncol = 1))
    rownames(pANN) <- real.cells
    colnames(pANN) <- "pANN"
    k <- round(nCells * pK)
    for (i in 1:n_real.cells) {
      neighbors <- order(dist.mat[, i])
      neighbors <- neighbors[2:(k + 1)]
      neighbor.names <- rownames(dist.mat)[neighbors]
      pANN$pANN[i] <- length(which(neighbors > n_real.cells))/k
    }

    print("Classifying doublets..")
    classifications <- rep("Singlet",n_real.cells)
    classifications[order(pANN$pANN[1:n_real.cells], decreasing=TRUE)[1:nExp]] <- "Doublet"
    seu@meta.data[, paste("pANN",pN,pK,nExp,sep="_")] <- pANN[rownames(seu@meta.data), 1]
    seu@meta.data[, paste("DF.classifications",pN,pK,nExp,sep="_")] <- classifications
    return(seu)
  }
}
## usage: https://rdrr.io/github/chris-mcginnis-ucsf/DoubletFinder/man/doubletFinder_v3.html
## source: https://github.com/chris-mcginnis-ucsf/DoubletFinder/blob/master/R/doubletFinder_v3.R

Run DoubletFinder

# the tutorial recommends using this as an approximation:
#nExp_poi <- round(0.15*nrow(pb_sex@meta.data))
#but a more appropriate approximation is that the expected number of doublets is ~1% per 1000 cells so:
nExp_poi <- round((0.01*(nrow(pb_sex@meta.data)/1000))*nrow(pb_sex@meta.data))
#run doublet finder:
pb_sex <- doubletFinder_v3(pb_sex, PCs = 1:21, pN = 0.25, pK = 0.01, nExp = nExp_poi, reuse.pANN = FALSE, sct = FALSE)
[1] "Creating 2210 artificial doublets..."
[1] "Creating Seurat object..."
[1] "Normalizing Seurat object..."
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
[1] "Finding variable genes..."
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
[1] "Scaling data..."
Centering and scaling data matrix

  |                                                                                           
  |                                                                                     |   0%
  |                                                                                           
  |==============                                                                       |  17%
  |                                                                                           
  |============================                                                         |  33%
  |                                                                                           
  |==========================================                                           |  50%
  |                                                                                           
  |=========================================================                            |  67%
  |                                                                                           
  |=======================================================================              |  83%
  |                                                                                           
  |=====================================================================================| 100%
[1] "Running PCA..."
[1] "Calculating PC distance matrix..."
[1] "Computing pANN..."
[1] "Classifying doublets.."

results in:

table(pb_sex@meta.data$DF.classifications_0.25_0.01_440)

Doublet Singlet 
    440    6191 

Validation and visualisation of doublets

visualise where doublets are:

doublet.cells <- c(rownames(pb_sex@meta.data[pb_sex@meta.data$DF.classifications_0.25_0.01_440 == "Doublet",]))
d1 <- DimPlot(pb_sex, reduction = "umap", cells.highlight = doublet.cells, sizes.highlight = 2)
#TSNEPlot(object = pb_sex, cells.highlight = doublet.cells, do.return = TRUE, )
doublet1 <- d1 + coord_fixed() + theme(axis.text.x=element_blank())

## plot clusters
cluster_plot <- DimPlot(pb_sex, reduction = "umap", group.by = "ident", label = TRUE)

doublet1 + cluster_plot

VlnPlot(object = pb_sex, features = "pANN_0.25_0.01_440", pt.size = 0.1)

## extract meta data cols of interest
df <- pb_sex@meta.data[,c("RNA_snn_res.1","DF.classifications_0.25_0.01_440")]

## make a table from doublets
df <- data.frame(rbind(table(df)))

## ammend rownames
df$cluster <- rownames(df)

## calculate percentages of cells that are doublets
df$pc_doublet <- ((df[,1])/((df[,1]) + df[,2]))*100

## inspect
#kable(df[order(df$pc_doublet),])

## plot 
ggplot(data=df, aes(x=cluster, y=pc_doublet)) +
  geom_col(fill="steelblue") +
  theme_minimal()

Filter doublets

It definitely seems like there are some biases in doublet detection. Fewer doublets in very early rings and in mature sexes may be due to a smaller number of the population being these cells.

It may also be biological, that these cells are less likely to associate to one another (although less likely, as doublets are a result of the probability of two cells being captured inside the same droplet together at a certain loading concentration, rather than two cells already being together upon droplet capture).

remove doublets:

## make a list of singlet cells
keep_singlets <- rownames(pb_sex@meta.data[pb_sex@meta.data$DF.classifications_0.25_0.01_440 == "Singlet",])

## subset into new seurat object
pb_sex_filtered <- subset(pb_sex, cells = keep_singlets, subset.raw = TRUE)
The following arguments are not used: subset.raw
## compare old and new objects
pb_sex
An object of class Seurat 
5098 features across 6631 samples within 1 assay 
Active assay: RNA (5098 features, 2000 variable features)
 2 dimensional reductions calculated: pca, umap
pb_sex_filtered
An object of class Seurat 
5098 features across 6191 samples within 1 assay 
Active assay: RNA (5098 features, 2000 variable features)
 2 dimensional reductions calculated: pca, umap

7. Life Cycle Stage (Using Bulk RNA-Seq Correlation)

Add in bulk data predictions

hoo et al.

#Pb Prediction correlations with bulk data (asexual hoo): 

#Load in required package:
library(Hmisc)
#Cooerce expression data into a matrix and load in the reference timecourse data:
x10 <- as.matrix(pb_sex_filtered@assays$RNA@data)
rownames(x10) <- gsub("-", "_", rownames(x10))
#read in bulk data:
hoo<-as.matrix(read.table("/Users/Andy/GCSKO/GCSKO_analysis_git/data/Reference/hoo_berg2.txt",header=T, row.names=1))
#Make a blank dataframe in which to add prediction:
df <- data.frame(matrix(ncol = 4, nrow = 0))
colnames(df) <- c("Prediction(Spearman)","r(Spearman)","Prediction(Pearsons)","r(Pearsons)")
#Do correlations with bulk data using both Spearman and Pearson (and the top 1000 genes):
for (i in 1:ncol(x10))
{
  shared<-intersect(row.names(as.matrix(head(sort(x10[,i], decreasing=TRUE),1000))),row.names(hoo))
  step0<-rcorr(x10[shared,i],hoo[shared,1:12],type = "spearman")
  step1<-as.matrix(t(step0$r[2:13,1]))
  step2<-rcorr(x10[shared,i],hoo[shared,1:12],type = "pearson")
  step3<-as.matrix(t(step2$r[2:13,1]))
  step4<-cbind(colnames(step1)[which.max(step1)],step1[which.max(step1)],colnames(step3)[which.max(step3)],step3[which.max(step3)])
  colnames(step4) <- c("Prediction(Spearman)","r(Spearman)","Prediction(Pearsons)","r(Pearsons)")
  rownames(step4)<-colnames(x10)[i]
  df<-rbind(df,step4)
}
#Write out data into a csv file:
#write.csv(dfringr,file="/Users/ar19/Desktop/PhD/AR04_GCSKO_project/All_mutants_Feb_2018/predictionpbcombined.csv")
#Change the format of the output to make it more readable:
#gsub("Pb_","", dfringr[,1]) - Make predictions into 18hr.dat format:

#spearman:
df[,1] <- gsub("Pb_","", df[,1])
#Remove hr.dat from list:
df[,1] <- gsub("hr.dat","", df[,1])
#Check - dfringr[,1]
#Make into a number:
df[,1] <- as.numeric(df[,1])
df[,2] <- as.numeric(as.character(df[,2]))

#pearson:
df[,3] <- gsub("Pb_","", df[,3])
#Remove hr.dat from list:
df[,3] <- gsub("hr.dat","", df[,3])
#Check - dfringr[,1]
#Make into a number:
df[,3] <- as.numeric(df[,3])
df[,4] <- as.numeric(as.character(df[,4]))
#add to 10X object:
pb_sex_filtered <- AddMetaData(pb_sex_filtered, metadata = df)
Invalid name supplied, making object name syntactically valid. New object name is Prediction.Spearman.r.Spearman.Prediction.Pearsons.r.Pearsons.; see ?make.names for more details on syntax validity

Kasia’s data

Can also do with Kasia’s timecourse data:

kas<-as.matrix(read.table("/Users/Andy/GCSKO/GCSKO_analysis_git/data/Reference/AP2OETC.txt",header=T, row.names=1))
#Make a blank dataframe in which to add prediction:
dfs <- data.frame(matrix(ncol = 4, nrow = 0))
colnames(dfs) <- c("ID","Prediction","r (Pearson)")
#Do correlations with bulk data using both Spearman and Pearson (and the top 1000 genes):
for (i in 1:ncol(x10))
{
  shared<-intersect(row.names(as.matrix(head(sort(x10[,i], decreasing=TRUE),1000))),rownames(kas))
  step0<-rcorr(x10[shared,i],kas[shared,1:10],type = "spearman")
  step1<-as.matrix(t(step0$r[2:11,1]))
  step2<-rcorr(x10[shared,i],kas[shared,1:10],type = "pearson")
  step3<-as.matrix(t(step2$r[2:11,1]))
  step4<-cbind(colnames(step1)[which.max(step1)],step1[which.max(step1)],colnames(step3)[which.max(step3)],step3[which.max(step3)])
  colnames(step4) <- c("Prediction(Spearman)","r(Spearman)","Prediction(Pearsons)","r(Pearsons)")
  rownames(step4)<-colnames(x10)[i]
  dfs<-rbind(dfs,step4)
}
#Write out data into a csv file:
#write.csv(df,file="/Users/ar19/Desktop/PhD/AR04_GCSKO_project/All_mutants_Feb_2018/predictionkasiacombined.csv")

#Change the format of the output to make it more readable:
#gsub("Pb_","", dfs[,1]) - Make predictions into 18hr.dat format:
dfs[,1] <- gsub("X","", dfs[,1])
#Make into a number:
dfs[,1] <- as.numeric(dfs[,1])
#Make into a number:
dfs[,2] <- as.numeric(as.character(dfs[,2]))

#gsub("Pb_","", dfs[,1]) - Make predictions into 18hr.dat format:
dfs[,3] <- gsub("X","", dfs[,3])
#Make into a number:
dfs[,3] <- as.numeric(dfs[,3])
#dfs[,1]
#Make into a number:
dfs[,4] <- as.numeric(as.character(dfs[,4]))

colnames(dfs) <- c('Prediction(Spearman)_Kasia', 'r(Spearman)_Kasia', 'Prediction(Pearson)_Kasia', 'r(Pearson)_Kasia')
#add to Seurat:
#add to 10X object:
pb_sex_filtered <- AddMetaData(pb_sex_filtered, dfs)
Invalid name supplied, making object name syntactically valid. New object name is Prediction.Spearman._Kasiar.Spearman._KasiaPrediction.Pearson._Kasiar.Pearson._Kasia; see ?make.names for more details on syntax validity

Visualise

Confirm life cycle designations:

## plot
FeaturePlot(pb_sex_filtered, features = c("Prediction.Spearman._Kasia", "Prediction.Spearman."))

optimse UMAP

## PCA calc
pb_sex_filtered <- RunPCA(pb_sex_filtered, features = VariableFeatures(object = pb_sex_filtered))
The following 5 features requested have zero variance (running reduction without them): PBANKA-0612861, PBANKA-0836921, PBANKA-1000041, PBANKA-API0290, PBANKA-API0031PC_ 1 
Positive:  PBANKA-1235600, PBANKA-0931300, PBANKA-1214300, PBANKA-1400400, PBANKA-1101100, PBANKA-0205000, PBANKA-0311800, PBANKA-0823400, PBANKA-1242300, PBANKA-1246600 
       PBANKA-0722600, PBANKA-1008500, PBANKA-0932400, PBANKA-1308600, PBANKA-1314800, PBANKA-1309900, PBANKA-1300096, PBANKA-1145400, PBANKA-1200096, PBANKA-1100441 
       PBANKA-0722801, PBANKA-1101300, PBANKA-1326400, PBANKA-1040521, PBANKA-0941300, PBANKA-1127000, PBANKA-1303800, PBANKA-0406500, PBANKA-1210200, PBANKA-0814200 
Negative:  PBANKA-1312700, PBANKA-1224900, PBANKA-0824400, PBANKA-1115200, PBANKA-1453000, PBANKA-1432200, PBANKA-1204200, PBANKA-0703500, PBANKA-0702000, PBANKA-0812600 
       PBANKA-0312700, PBANKA-1128800, PBANKA-1218300, PBANKA-1232600, PBANKA-1432400, PBANKA-1430600, PBANKA-1449300, PBANKA-1106500, PBANKA-0209300, PBANKA-0620400 
       PBANKA-1421500, PBANKA-1336200, PBANKA-1320800, PBANKA-0417600, PBANKA-1038800, PBANKA-1129800, PBANKA-1414500, PBANKA-1451100, PBANKA-1143800, PBANKA-0402700 
PC_ 2 
Positive:  PBANKA-1431500, PBANKA-1431400, PBANKA-0942400, PBANKA-1340400, PBANKA-1449000, PBANKA-1038700, PBANKA-0942300, PBANKA-0934700, PBANKA-1208800, PBANKA-0828900 
       PBANKA-0911000, PBANKA-0306100, PBANKA-0927700, PBANKA-0806800, PBANKA-1409600, PBANKA-0406200, PBANKA-0411300, PBANKA-0415800, PBANKA-0507800, PBANKA-0416100 
       PBANKA-1033700, PBANKA-1361500, PBANKA-1108800, PBANKA-0920700, PBANKA-0510600, PBANKA-0925400, PBANKA-0720100, PBANKA-0507300, PBANKA-1119800, PBANKA-1030400 
Negative:  PBANKA-1317200, PBANKA-1352500, PBANKA-0827400, PBANKA-0810700, PBANKA-1225500, PBANKA-1319500, PBANKA-0402500, PBANKA-1419300, PBANKA-0714000, PBANKA-1315300 
       PBANKA-1436300, PBANKA-0105100, PBANKA-1311400, PBANKA-0821400, PBANKA-0417600, PBANKA-0204500, PBANKA-1342300, PBANKA-0417200, PBANKA-0605800, PBANKA-0704900 
       PBANKA-0517600, PBANKA-1231300, PBANKA-1329900, PBANKA-1415200, PBANKA-1115000, PBANKA-1134900, PBANKA-1035200, PBANKA-1233600, PBANKA-0907100, PBANKA-0912500 
PC_ 3 
Positive:  PBANKA-1400600, PBANKA-1459300, PBANKA-0416000, PBANKA-0305100, PBANKA-0304800, PBANKA-1443300, PBANKA-1365200, PBANKA-0830200, PBANKA-0938400, PBANKA-1349000 
       PBANKA-0827200, PBANKA-1137800, PBANKA-1437300, PBANKA-1113300, PBANKA-0932200, PBANKA-0506100, PBANKA-1017100, PBANKA-0313800, PBANKA-0408500, PBANKA-1117000 
       PBANKA-0409800, PBANKA-0922500, PBANKA-0519400, PBANKA-0925600, PBANKA-0316600, PBANKA-0915200, PBANKA-0722921, PBANKA-0934300, PBANKA-0941800, PBANKA-1225000 
Negative:  PBANKA-1431500, PBANKA-1431400, PBANKA-1449000, PBANKA-0806800, PBANKA-1108000, PBANKA-0927700, PBANKA-0911000, PBANKA-0828900, PBANKA-0601200, PBANKA-1208800 
       PBANKA-1409600, PBANKA-0830900, PBANKA-0521200, PBANKA-1029400, PBANKA-0102700, PBANKA-0942400, PBANKA-1304500, PBANKA-1316500, PBANKA-1038700, PBANKA-1333100 
       PBANKA-0902400, PBANKA-1109600, PBANKA-1129600, PBANKA-1119800, PBANKA-0822900, PBANKA-0719700, PBANKA-1429100, PBANKA-1038200, PBANKA-0301800, PBANKA-1320100 
PC_ 4 
Positive:  PBANKA-0519000, PBANKA-1349100, PBANKA-0519100, PBANKA-0519300, PBANKA-1344400, PBANKA-0831000, PBANKA-1002400, PBANKA-0713100, PBANKA-0523700, PBANKA-0111000 
       PBANKA-1032100, PBANKA-1014500, PBANKA-0519400, PBANKA-0932000, PBANKA-0804500, PBANKA-1349000, PBANKA-1315700, PBANKA-0501400, PBANKA-1101400, PBANKA-1425900 
       PBANKA-0519200, PBANKA-1210600, PBANKA-0509600, PBANKA-0619700, PBANKA-1327100, PBANKA-0523800, PBANKA-0307500, PBANKA-1035400, PBANKA-1119600, PBANKA-1117200 
Negative:  PBANKA-0107300, PBANKA-0814200, PBANKA-1214300, PBANKA-0713300, PBANKA-0604300, PBANKA-0405200, PBANKA-0932200, PBANKA-0109100, PBANKA-0823400, PBANKA-0938400 
       PBANKA-0416500, PBANKA-1460400, PBANKA-1437300, PBANKA-1302800, PBANKA-1130500, PBANKA-0406500, PBANKA-1448000, PBANKA-0211500, PBANKA-1444100, PBANKA-1450300 
       PBANKA-0710100, PBANKA-1223100, PBANKA-1419100, PBANKA-0313800, PBANKA-0818900, PBANKA-0919600, PBANKA-1235600, PBANKA-1242300, PBANKA-0916200, PBANKA-1203700 
PC_ 5 
Positive:  PBANKA-0501600, PBANKA-1240600, PBANKA-0707700, PBANKA-0112200, PBANKA-1002600, PBANKA-0208900, PBANKA-0812200, PBANKA-1423000, PBANKA-0832800, PBANKA-1409200 
       PBANKA-0307500, PBANKA-0716900, PBANKA-0100400, PBANKA-1002400, PBANKA-0915000, PBANKA-1364400, PBANKA-1119600, PBANKA-1347000, PBANKA-0819600, PBANKA-1319000 
       PBANKA-1212500, PBANKA-0900900, PBANKA-1202000, PBANKA-1400091, PBANKA-1228800, PBANKA-0911700, PBANKA-0311500, PBANKA-1345900, PBANKA-1025300, PBANKA-0519200 
Negative:  PBANKA-1443300, PBANKA-0925600, PBANKA-0306700, PBANKA-0907200, PBANKA-1349000, PBANKA-1106500, PBANKA-1217400, PBANKA-1107600, PBANKA-1319300, PBANKA-0304800 
       PBANKA-0519400, PBANKA-0312500, PBANKA-1313100, PBANKA-0305100, PBANKA-0112100, PBANKA-0611700, PBANKA-1241800, PBANKA-0304400, PBANKA-0833300, PBANKA-0208000 
       PBANKA-1113300, PBANKA-1339300, PBANKA-1438000, PBANKA-0206300, PBANKA-1340500, PBANKA-0207500, PBANKA-0915200, PBANKA-0808000, PBANKA-1020100, PBANKA-1360000 
## elbow plot
ElbowPlot(pb_sex_filtered, ndims = 30, reduction = "pca")


## UMAP calc
pb_sex_filtered <- RunUMAP(pb_sex_filtered, dims = 1:8, seed.use = 300, n.neighbors = 60, min.dist = 0.5, repulsion.strength = 0.05, local.connectivity = 20)
16:11:27 UMAP embedding parameters a = 0.583 b = 1.334
16:11:27 Read 6191 rows and found 8 numeric columns
16:11:27 Using Annoy for neighbor search, n_neighbors = 60
16:11:27 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
16:11:29 Writing NN index file to temp file /var/folders/wj/rztzclxn1t10cl2sk0plbf3r0000gn/T//RtmpyuC0hm/fileef9560289fdd
16:11:30 Searching Annoy index using 1 thread, search_k = 6000
16:11:36 Annoy recall = 100%
16:11:39 Commencing smooth kNN distance calibration using 1 thread
16:11:39 6191 smooth knn distance failures
16:11:43 Initializing from normalized Laplacian + noise
16:11:45 Commencing optimization for 500 epochs, with 184004 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
16:12:22 Optimization finished
## UMAP plot
DimPlot(pb_sex_filtered, reduction = "umap", group.by = "ident", label = TRUE)

8. Save and Export

Save environment

## This saves everything in the global environment for easy recall later
#save.image(file = "GCSKO_10X_QC.RData")
#load(file = "GCSKO_10X_QC.RData")

Save object(s)

## save Rdata
#save(pb_30k_sex_filtered, pb_sex_filtered, file = "Part_2_input.Rdata")
#load(file = "Part_2_input.Rdata")

## save RDS
saveRDS(pb_sex_filtered, file = "/Users/Andy/GCSKO/GCSKO_analysis_git/data_to_export/pb_sex_filtered.RDS", compress = FALSE) 
#pb_sex_filtered <- readRDS("pb_sex_filtered.RDS")

## Save Robj
#save(pb_sex_filtered,file="pb_sex_filtered.Robj")

Appendix

Session Info

R version 4.0.2 (2020-06-22)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Catalina 10.15.6

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib

locale:
[1] en_GB.UTF-8/en_GB.UTF-8/en_GB.UTF-8/C/en_GB.UTF-8/en_GB.UTF-8

attached base packages:
[1] grid      stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] viridis_0.5.1      viridisLite_0.3.0  KernSmooth_2.23-17 fields_10.3       
 [5] maps_3.3.0         spam_2.5-1         dotCall64_1.0-0    ggridges_0.5.2    
 [9] patchwork_1.0.1    ggpubr_0.4.0       scales_1.1.1       mixtools_1.2.0    
[13] dplyr_1.0.0        Hmisc_4.4-0        ggplot2_3.3.2      Formula_1.2-3     
[17] survival_3.2-3     lattice_0.20-41    gridExtra_2.3      cowplot_1.0.0     
[21] Seurat_3.2.0       knitr_1.29        

loaded via a namespace (and not attached):
  [1] readxl_1.3.1          backports_1.1.8       plyr_1.8.6            igraph_1.2.5         
  [5] lazyeval_0.2.2        splines_4.0.2         listenv_0.8.0         usethis_1.6.1        
  [9] digest_0.6.25         htmltools_0.5.0       fansi_0.4.1           magrittr_1.5         
 [13] checkmate_2.0.0       memoise_1.1.0         tensor_1.5            cluster_2.1.0        
 [17] ROCR_1.0-11           openxlsx_4.1.5        remotes_2.1.1         globals_0.12.5       
 [21] prettyunits_1.1.1     jpeg_0.1-8.1          colorspace_1.4-1      ggrepel_0.8.2        
 [25] haven_2.3.1           xfun_0.15             callr_3.4.3           crayon_1.3.4         
 [29] jsonlite_1.7.0        spatstat_1.64-1       spatstat.data_1.4-3   zoo_1.8-8            
 [33] ape_5.4               glue_1.4.1            polyclip_1.10-0       gtable_0.3.0         
 [37] leiden_0.3.3          car_3.0-8             pkgbuild_1.1.0        kernlab_0.9-29       
 [41] future.apply_1.6.0    abind_1.4-5           rstatix_0.6.0         miniUI_0.1.1.1       
 [45] Rcpp_1.0.5            xtable_1.8-4          htmlTable_2.0.1       reticulate_1.16      
 [49] foreign_0.8-80        rsvd_1.0.3            htmlwidgets_1.5.1     httr_1.4.2           
 [53] RColorBrewer_1.1-2    acepack_1.4.1         ellipsis_0.3.1        ica_1.0-2            
 [57] farver_2.0.3          pkgconfig_2.0.3       nnet_7.3-14           uwot_0.1.8           
 [61] deldir_0.1-28         labeling_0.3          tidyselect_1.1.0      rlang_0.4.7          
 [65] reshape2_1.4.4        later_1.1.0.1         cellranger_1.1.0      munsell_0.5.0        
 [69] tools_4.0.2           cli_2.0.2             generics_0.0.2        broom_0.7.0          
 [73] devtools_2.3.0        evaluate_0.14         stringr_1.4.0         fastmap_1.0.1        
 [77] yaml_2.2.1            goftest_1.2-2         processx_3.4.3        fs_1.4.2             
 [81] fitdistrplus_1.1-1    zip_2.1.0             purrr_0.3.4           RANN_2.6.1           
 [85] pbapply_1.4-2         future_1.18.0         nlme_3.1-148          mime_0.9             
 [89] compiler_4.0.2        rstudioapi_0.11       curl_4.3              plotly_4.9.2.1       
 [93] png_0.1-7             ggsignif_0.6.0        testthat_2.3.2        spatstat.utils_1.17-0
 [97] tibble_3.0.3          stringi_1.4.6         highr_0.8             ps_1.3.3             
[101] RSpectra_0.16-0       desc_1.2.0            forcats_0.5.0         Matrix_1.2-18        
[105] vctrs_0.3.2           pillar_1.4.6          lifecycle_0.2.0       lmtest_0.9-37        
[109] RcppAnnoy_0.0.16      data.table_1.12.8     irlba_2.3.3           httpuv_1.5.4         
[113] R6_2.4.1              latticeExtra_0.6-29   promises_1.1.1        rio_0.5.16           
[117] sessioninfo_1.1.1     codetools_0.2-16      MASS_7.3-51.6         assertthat_0.2.1     
[121] pkgload_1.1.0         rprojroot_1.3-2       withr_2.2.0           sctransform_0.2.1    
[125] hms_0.5.3             mgcv_1.8-31           parallel_4.0.2        rpart_4.1-15         
[129] tidyr_1.1.0           rmarkdown_2.3         carData_3.0-4         segmented_1.2-0      
[133] Rtsne_0.15            shiny_1.5.0           base64enc_0.1-3       tinytex_0.25         
LS0tCnN1YnRpdGxlOiAnR2FtZXRvY3l0ZSBEZXZlbG9wbWVudCBpbiA8aT5QbGFzbW9kaXVtIGJlcmdoZWk8L2k+Jwp0aXRsZTogfAogICFbXSgvVXNlcnMvQW5keS9HQ1NLTy9HQ1NLT19hbmFseXNpc19naXQvR0NTS09fbG9nby5qcGcpe3dpZHRoPTMwMHB4fSAgCiAgMTBYIFF1YWxpdHkgQ29udHJvbAphdXRob3I6ICJbQW5kcmV3IFJ1c3NlbGxdKGh0dHBzOi8vYWpjcnVzc2VsbC53aXhzaXRlLmNvbS9teXNpdGUvYWJvdXQpIgppbnN0aXR1dGU6IFdlbGxjb21lIFNhbmdlciBJbnN0aXR1dGUKZGF0ZTogJ2ByIGZvcm1hdChTeXMuRGF0ZSgpLCAiJUIgJWQsICVZIilgJwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRoZW1lOiBjb3NtbwogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogMwogICAgI3RvY19mbG9hdDogeWVzCiAgICBkZl9wcmludDogcGFnZWQKLS0tCioqKgojIDEuIEludHJvZHVjdGlvbiBhbmQgQWltcyB7LnRhYnNldH0KClR3byBkYXRhc2V0cyB3ZXJlIGdlbmVyYXRlZCB1c2luZyB0aGUgMTBYIEdlbm9taWNzIENocm9taXVtIDMnIHNjUk5BLVNlcSBwbGF0Zm9ybToKCmBgYHtyIGludHJvLCBlY2hvPUZBTFNFLCByZXN1bHRzPSdhc2lzJ30KIyMgbG9hZCBrbml0ciB0byBkaXNwbGF5IHRhYmxlCmxpYnJhcnkoa25pdHIpCiMjIG1ha2UgZGF0YWZyYW1lCnNwZWNpZXMgPC0gYygicGIiLCAicGIiKQpleHBlcmltZW50X25hbWUgPC0gYygnc3RyYWlnaHQgYmxlZWQgZXhwZXJpbWVudCcsJzE6MSBtaXggZXhwZXJpbWVudCcpCnJ1bl9udW1iZXIgPC0gYygiMjIyNTIiLCAiMjQyODQiKQpsYW5lX251bWJlciA8LSBjKCI1IiwgIjEgJiAyIikKc2VxdWVuY2VyIDwtIGMoIkhpc2VxIDQwMDAiLCAiSGlzZXEgMjUwMCIpCmFwcHJveGltYXRlX251bWJlcl9vZl9jZWxscyA8LSBjKCIzMCwwMDAiLCAiNSwwMDAiKQplbXBsb3kuZGF0YSA8LSBkYXRhLmZyYW1lKHNwZWNpZXMsIGV4cGVyaW1lbnRfbmFtZSwgcnVuX251bWJlciwgbGFuZV9udW1iZXIsIHNlcXVlbmNlciwgYXBwcm94aW1hdGVfbnVtYmVyX29mX2NlbGxzLCBzdHJpbmdzQXNGYWN0b3JzPUZBTFNFKQojIyBwcmludCBkYXRhZnJhbWUKa2FibGUoZW1wbG95LmRhdGEpCmBgYAoKVGhpcyBkYXRhIGhhcyBiZWVuIHByb2Nlc3NlZCB1c2luZyBDZWxsUmFuZ2VyIGludG8gY291bnRzIHRhYmxlcy4gVGhpcyBpbml0aWFsIGFuYWx5c2lzIGdhdmUgdGhlIGZvbGxvd2luZyBtZXRyaWNzOgoKPGI+UGIgMToxIG1peCBleHBlcmltZW50IChydW4gIzogMjQyODQgbGFuZXMgMSBhbmQgMiAoSGlzZXEgMjUwMCkpOjwvYj4KCjxkaXYgc3R5bGU9IndpZHRoOjUwMHB4OyBoZWlnaHQ6NDAwcHgiPiFbXSgvVXNlcnMvQW5keS9HQ1NLTy9HQ1NLT19hbmFseXNpc19naXQvZGF0YS8xMFgvMjQyODRfY2VsbHJhbmdlcl9vdXRwdXRfMS5wbmcpPC9kaXY+CjxkaXYgc3R5bGU9IndpZHRoOjUwMHB4OyBoZWlnaHQ6MjAwcHgiPiFbXSgvVXNlcnMvQW5keS9HQ1NLTy9HQ1NLT19hbmFseXNpc19naXQvZGF0YS8xMFgvMjQyODRfY2VsbHJhbmdlcl9vdXRwdXRfMi5wbmcpPC9kaXY+Cgo8Yj5XZSB3aWxsIGxvYWQgdGhpcyBkYXRhIGluIGFuZCBmb3IgZWFjaCBydW46PC9iPgoKQS4gRGVmaW5lICdjZWxscycKCkIuIEZpbHRlciBwb29yIHF1YWxpdHkgY2VsbHMgb3V0CgpDLiBEaW1lbnNpb25hbGl0eSBSZWR1Y3Rpb24gYW5kIENsdXN0ZXJpbmcgCgpELiBSZW1vdmUgRG91YmxldHMKCkUuIFByZWRpY3QgbGlmZSBDeWNsZSBTdGFnZSAoVXNpbmcgQnVsayBSTkEtU2VxIENvcnJlbGF0aW9uKQoKIyAyLiBSZWFkIGluIHRoZSBkYXRhICB7LnRhYnNldH0KCiMjIyBMb2FkIHRoZSByZXF1aXJlZCBwYWNrYWdlcwoKYGBge3IgbG9hZCBwYWNrYWdlcywgZWNobyA9IEZBTFNFfQojIyBTZXVyYXQgaXMgbmVlZGVkIGZvciBtb3N0IG9mIHRoaXMgc2NyaXB0CmlmKHJlcXVpcmUoIlNldXJhdCIsIHF1aWV0bHkgPSBUUlVFKSl7CiAgICBwcmludCgiU2V1cmF0IGlzIGxvYWRlZCBjb3JyZWN0bHkiKQp9IGVsc2UgewogICAgcHJpbnQoInRyeWluZyB0byBpbnN0YWxsIFNldXJhdCIpCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJTZXVyYXQiKQogICAgaWYocmVxdWlyZShTZXVyYXQpKXsKICAgICAgICBwcmludCgiU2V1cmF0IGluc3RhbGxlZCBhbmQgbG9hZGVkIikKICAgIH0gZWxzZSB7CiAgICAgICAgc3RvcCgiY291bGQgbm90IGluc3RhbGwgU2V1cmF0IikKICAgIH0KfQoKIyMgY293cGxvdCBpcyBuZWVkZWQgZm9yIHBsb3RzCmlmKHJlcXVpcmUoImNvd3Bsb3QiKSl7CiAgICBwcmludCgiY293cGxvdCBpcyBsb2FkZWQgY29ycmVjdGx5IikKfSBlbHNlIHsKICAgIHByaW50KCJ0cnlpbmcgdG8gaW5zdGFsbCBjb3dwbG90IikKICAgIGluc3RhbGwucGFja2FnZXMoImNvd3Bsb3QiKQogICAgaWYocmVxdWlyZShjb3dwbG90KSl7CiAgICAgICAgcHJpbnQoImNvd3Bsb3QgaW5zdGFsbGVkIGFuZCBsb2FkZWQiKQogICAgfSBlbHNlIHsKICAgICAgICBzdG9wKCJjb3VsZCBub3QgaW5zdGFsbCBjb3dwbG90IikKICAgIH0KfQoKIyMgZ3JpZEV4dHJhIGlzIG5lZWRlZCBmb3IgZ3JpZCBncmFwaGljcyB0byBwbG90IG11bHRpcGxlIHBsb3RzIGluIHRoZSBzYW1lIHZpZXcKaWYocmVxdWlyZSgiZ3JpZEV4dHJhIikpewogICAgcHJpbnQoImdyaWRFeHRyYSBpcyBsb2FkZWQgY29ycmVjdGx5IikKfSBlbHNlIHsKICAgIHByaW50KCJ0cnlpbmcgdG8gaW5zdGFsbCBncmlkRXh0cmEiKQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ3JpZEV4dHJhIikKICAgIGlmKHJlcXVpcmUoZ3JpZEV4dHJhKSl7CiAgICAgICAgcHJpbnQoImdyaWRFeHRyYSBpbnN0YWxsZWQgYW5kIGxvYWRlZCIpCiAgICB9IGVsc2UgewogICAgICAgIHN0b3AoImNvdWxkIG5vdCBpbnN0YWxsIGdyaWRFeHRyYSIpCiAgICB9Cn0KCiMjZm9yIGdyaWQuYXJyYW5nZSBmdW5jdGlvbiB0byBjaGFuZ2Ugc2l6ZSBvZiB0aXRsZQppZihyZXF1aXJlKCJncmlkIikpewogICAgcHJpbnQoImdyaWQgaXMgbG9hZGVkIGNvcnJlY3RseSIpCn0gZWxzZSB7CiAgICBwcmludCgidHJ5aW5nIHRvIGluc3RhbGwgZ3JpZCIpCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJncmlkIikKICAgIGlmKHJlcXVpcmUoZ3JpZCkpewogICAgICAgIHByaW50KCJncmlkIGluc3RhbGxlZCBhbmQgbG9hZGVkIikKICAgIH0gZWxzZSB7CiAgICAgICAgc3RvcCgiY291bGQgbm90IGluc3RhbGwgZ3JpZCIpCiAgICB9Cn0KCiMjIGZvciBkb2luZyBidWxrIGNvcnJlbGF0aW9uIGNhbGN1bGF0aW9ucwppZihyZXF1aXJlKCJIbWlzYyIpKXsKICAgIHByaW50KCJIbWlzYyBpcyBsb2FkZWQgY29ycmVjdGx5IikKfSBlbHNlIHsKICAgIHByaW50KCJ0cnlpbmcgdG8gaW5zdGFsbCBIbWlzYyIpCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJIbWlzYyIpCiAgICBpZihyZXF1aXJlKEhtaXNjKSl7CiAgICAgICAgcHJpbnQoIkhtaXNjIGluc3RhbGxlZCBhbmQgbG9hZGVkIikKICAgIH0gZWxzZSB7CiAgICAgICAgc3RvcCgiY291bGQgbm90IGluc3RhbGwgSG1pc2MiKQogICAgfQp9CgojIyBkcGx5ciBpcyBuZWVkZWQgdG8gd29yayB3aXRoIGRhdGEgZnJhbWVzCmlmKHJlcXVpcmUoImRwbHlyIikpewogICAgcHJpbnQoImRwbHlyIGlzIGxvYWRlZCBjb3JyZWN0bHkiKQp9IGVsc2UgewogICAgcHJpbnQoInRyeWluZyB0byBpbnN0YWxsIGRwbHlyIikKICAgIGluc3RhbGwucGFja2FnZXMoImRwbHlyIikKICAgIGlmKHJlcXVpcmUoZHBseXIpKXsKICAgICAgICBwcmludCgiZHBseXIgaW5zdGFsbGVkIGFuZCBsb2FkZWQiKQogICAgfSBlbHNlIHsKICAgICAgICBzdG9wKCJjb3VsZCBub3QgaW5zdGFsbCBkcGx5ciIpCiAgICB9Cn0KCiMjIHNjYWxlcyBpcyBuZWVkZWQgZm9yIGJyZWFrIGZvcm1hdHRpbmcgZnVuY3Rpb25zIGluIHRoZSBiYXJjb2RlIHBsb3QKaWYocmVxdWlyZSgic2NhbGVzIikpewogICAgcHJpbnQoInNjYWxlcyBpcyBsb2FkZWQgY29ycmVjdGx5IikKfSBlbHNlIHsKICAgIHByaW50KCJ0cnlpbmcgdG8gaW5zdGFsbCBzY2FsZXMiKQogICAgaW5zdGFsbC5wYWNrYWdlcygic2NhbGVzIikKICAgIGlmKHJlcXVpcmUoc2NhbGVzKSl7CiAgICAgICAgcHJpbnQoInNjYWxlcyBpbnN0YWxsZWQgYW5kIGxvYWRlZCIpCiAgICB9IGVsc2UgewogICAgICAgIHN0b3AoImNvdWxkIG5vdCBpbnN0YWxsIHNjYWxlcyIpCiAgICB9Cn0KCiMjIGdncHViciBpcyBuZWVkZWQgZm9yIHBsb3R0aW5nCmlmKHJlcXVpcmUoImdncHViciIsIHF1aWV0bHkgPSBUUlVFKSl7CiAgICBwcmludCgiZ2dwdWJyIGlzIGxvYWRlZCBjb3JyZWN0bHkiKQp9IGVsc2UgewogICAgcHJpbnQoInRyeWluZyB0byBpbnN0YWxsIGdncHViciIpCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3B1YnIiKQogICAgaWYocmVxdWlyZShnZ3B1YnIpKXsKICAgICAgICBwcmludCgiZ2dwdWJyIGluc3RhbGxlZCBhbmQgbG9hZGVkIikKICAgIH0gZWxzZSB7CiAgICAgICAgc3RvcCgiY291bGQgbm90IGluc3RhbGwgZ2dwdWJyIikKICAgIH0KfQoKIyMgcGF0Y2h3b3JrIGlzIG5lZWRlZCBmb3IgcGxvdHRpbmcKIyMgV0FSTklORyEgY293cGxvdCBvdmVyLXJpZGVzIHRoaXMgYnkgbWFza2luZyBpdCBzbyBiZSBjYXJlZnVsLgppZihyZXF1aXJlKCJwYXRjaHdvcmsiLCBxdWlldGx5ID0gVFJVRSkpewogICAgcHJpbnQoInBhdGNod29yayBpcyBsb2FkZWQgY29ycmVjdGx5IikKfSBlbHNlIHsKICAgIHByaW50KCJ0cnlpbmcgdG8gaW5zdGFsbCBwYXRjaHdvcmsiKQogICAgaW5zdGFsbC5wYWNrYWdlcygicGF0Y2h3b3JrIikKICAgIGlmKHJlcXVpcmUocGF0Y2h3b3JrKSl7CiAgICAgICAgcHJpbnQoInBhdGNod29yayBpbnN0YWxsZWQgYW5kIGxvYWRlZCIpCiAgICB9IGVsc2UgewogICAgICAgIHN0b3AoImNvdWxkIG5vdCBpbnN0YWxsIHBhdGNod29yayIpCiAgICB9Cn0KCiMjIHNldCB0aGUgc2VlZCBmb3IgYm90aCB0aGUgbWl4dHVyZSBtb2RlbHMgYW5kIGFsc28gZm9yIHRoZSBzYW1wbGUgZnVuY3Rpb24gbGF0ZXIgb246CnNldC5zZWVkKC05MjQ5NykKYGBgCgojIyMgSW1wb3J0IEdURiBmaWxlCgpUaGlzIHdpbGwgYmUgaGVscGZ1bCBsYXRlciBvbi4gVGhpcyBjb250YWlucyBhbm5vdGF0aW9ucyBmb3IgZWFjaCBnZW5lOgpgYGB7ciBpbXBvcnQgZ3RmfQojI0ltcG9ydCBndGYgZmlsZToKZ3RmIDwtIHJlYWQudGFibGUoIi9Vc2Vycy9BbmR5L0dDU0tPL0dDU0tPX2FuYWx5c2lzX2dpdC9kYXRhL1JlZmVyZW5jZS9QYmVyZ2hlaS5ndGYiLCBzZXA9Ilx0IiwgaGVhZGVyID0gRkFMU0UpCmhlYWQoZ3RmKQpgYGAKCiMjIyBSZWFkIGluIHRoZSBEYXRhCgpgYGB7ciBpbXBvcnQgZGF0YX0KIyMgcmVhZCBpbiAxMHggb3V0cHV0IAp0ZW54NWtfcmF3X2RhdGEgPC0gUmVhZDEwWCgiL1VzZXJzL0FuZHkvR0NTS08vR0NTS09fYW5hbHlzaXNfZ2l0L2RhdGEvMTBYL3RlbnhfMjQyODQiKQoKIyMgQ3JlYXRlIFNldXJhdCBvYmplY3QKdGVueDVrIDwtIENyZWF0ZVNldXJhdE9iamVjdChjb3VudHMgPSB0ZW54NWtfcmF3X2RhdGEsIG1pbi5jZWxscyA9IDAsIG1pbi5mZWF0dXJlcyA9IDAsIHByb2plY3QgPSAiR0NTS08iKQoKIyMgYWRkIGV4cGVyaW1lbnQgdG8gbWV0YSBkYXRhIGZvciBtZXJnaW5nIGxhdGVyCnRlbng1a0BtZXRhLmRhdGEkZXhwZXJpbWVudCA8LSAidGVueDVrIgpgYGAKCiMgMy4gRGVmaW5pbmcgQ2VsbHMgdnMuIEJhY2tncm91bmQgey50YWJzZXR9CgpQbG90IGEga25lZSBwbG90IGFuZCB0aGVuIHVzZSBhIG1peHR1cmUgbW9kZWwgdG8gZGVmaW5lIHdoZXJlIHRoZSBjZWxscyB2cy4gYmFja2dyb3VuZCBsaWUKYGBge3IgbWl4bW9kZWwgc2V0dXB9CiMjIGludGVyZXN0aW5nIHJlZmVyZW5jZSBtYXRlcmlhbCBmb3IgdGhpcyBzZWN0aW9uIGNhbiBiZSBmb3VuZCBoZXJlOiBodHRwczovL2hlbWJlcmctbGFiLmdpdGh1Yi5pby9zY1JOQS5zZXEuY291cnNlL3Byb2Nlc3NpbmctcmF3LXNjcm5hLXNlcS1kYXRhLmh0bWwgCgojIyBnZXQgdGhlIG5VTUlzCnVtaV9wZXJfYmFyY29kZSA8LSBhcy5kYXRhLmZyYW1lKHRlbng1a0BtZXRhLmRhdGEkbkNvdW50X1JOQSkKCiMjIHJlbW92ZSB6ZXJvcyBhcyB0aGVzZSBoYXZlIGlzc3VlcyB3aGVuIHlvdSBsb2cgdGhlbSBhbmQgbWFrZSB0aGUgbW9kZWwgbGF0ZXI6CnVtaV9wZXJfYmFyY29kZSA8LSBhcy5kYXRhLmZyYW1lKHVtaV9wZXJfYmFyY29kZVshKHVtaV9wZXJfYmFyY29kZSRgdGVueDVrQG1ldGEuZGF0YSRuQ291bnRfUk5BYD09MCksIF0pCgojIyBnZXQgYSByYW5rIGZvciBlYWNoIGJhcmNvZGUKYmFyY29kZV9yYW5rIDwtIHJhbmsoLXVtaV9wZXJfYmFyY29kZVssMV0pCgojIyB0aGVuIG1ha2UgaW50byBhIGxpc3QKbGliX3NpemUgPC0gKHVtaV9wZXJfYmFyY29kZVssMV0pCgojIyB0aGVuIGxvZyB0aGlzCmxvZ19saWJfc2l6ZSA8LSBsb2cxMCh1bWlfcGVyX2JhcmNvZGVbLDFdKQoKIyNwbG90CiNwbG90KGJhcmNvZGVfcmFuaywgbG9nX2xpYl9zaXplLCB4bGltPWMoMSwxMDAwMDApKQoKIyMgb3JkZXIgdGhlIGJhcmNvZGUgcmFua3MKbyA8LSBvcmRlcihiYXJjb2RlX3JhbmspCgojIyByZW9yZGVyIHRoZSBsaWJyYXJ5IHNpemUsIGJhcmNvZGUgcmFuayBieSB0aGVpciByYW5rCmxvZ19saWJfc2l6ZSA8LSBsb2dfbGliX3NpemVbb10KYmFyY29kZV9yYW5rIDwtIGJhcmNvZGVfcmFua1tvXQpsaWJfc2l6ZSA8LSBsaWJfc2l6ZVtvXQpgYGAKCm1ha2UgYSBtaXh0dXJlIG1vZGVsIHRvIGRldGVybWluZSB0aGUga25lZSBvZiB0aGUgcGxvdApgYGB7ciBtaXhtb2RlbCBjYWxjfQojIyBzZXQgYSBzZWVkIGZvciB0aGUgbWl4dHVyZSBtb2RlbApzZXQuc2VlZCgtOTI0OTcpCgojIyBtaXh0dXJlIG1vZGVsIGNhbGN1bGF0aW9uIApyZXF1aXJlKCJtaXh0b29scyIpCm1peCA8LSBub3JtYWxtaXhFTShsb2dfbGliX3NpemUpCgojIyBwbG90IHJlc3VsdApwbG90KG1peCwgd2hpY2g9MiwgeGxhYjI9ImxvZyhtb2wgcGVyIGNlbGwpIikKYGBgCgpGaW5kIHdoZXJlIHRoZSBkaXN0cmlidXRpb25zIGludGVyc2VjdCAoaS5lLiB3aGVyZSBjZWxscyB2cy4gYmFja2dyb3VuZCBpcykKYGBge3IgY2FsY3VsYXRlIHNwbGl0fQojIyBpZGVudGlmeSB3aGVyZSB0aGUgc3BsaXQgYmV0d2VlbiB0aGUgZGlzdHJpYnV0aW9ucyBpcwpwMSA8LSBkbm9ybShsb2dfbGliX3NpemUsIG1lYW49bWl4JG11WzFdLCBzZD1taXgkc2lnbWFbMV0pCnAyIDwtIGRub3JtKGxvZ19saWJfc2l6ZSwgbWVhbj1taXgkbXVbMl0sIHNkPW1peCRzaWdtYVsyXSkKaWYgKG1peCRtdVsxXSA8IG1peCRtdVsyXSkgewogICAgc3BsaXQgPC0gbWluKGxvZ19saWJfc2l6ZVtwMiA+IHAxXSkKfSBlbHNlIHsKICAgIHNwbGl0IDwtIG1pbihsb2dfbGliX3NpemVbcDEgPiBwMl0pCn0KCiMjIHByaW50IHNwbGl0CnNwbGl0CmBgYAoKVmlldyB0aGUgaW5pdGlhbCByZXN1bHQKYGBge3J9CiMjIGxvZyB0aGUgYmFyY29kZSByYW5rCmxvZ19iYXJjb2RlX3JhbmsgPC0gbG9nMTAoYmFyY29kZV9yYW5rKQoKIyMgcGxvdApwbG90KGxvZ19iYXJjb2RlX3JhbmssIGxvZ19saWJfc2l6ZSwgeGxpbT1jKDEsNikpCiMjIGFkZCB0aGUgc3BsaXQgYXMgYSBsaW5lIG9uIHRoZSBwbG90CmFibGluZShoPXNwbGl0LCBjb2w9InJlZCIpCmBgYAoKRmluYWwgRmlndXJlczoKYGBge3IgYmFyY29kZSBwbG90fQojIyBtYWtlIHRoZSByZXN1bHRzIG9mIHRoZSBhYm92ZSBmdW5jdGlvbnMgaW50byBhIGRhdGFmcmFtZQpkZl9iYXJjb2RlcyA8LSBhcy5kYXRhLmZyYW1lKGNiaW5kKGJhcmNvZGVfcmFuaywgbG9nX2xpYl9zaXplLCBsaWJfc2l6ZSksIHJvdy5uYW1lcyA9IE5VTEwpCgojIyBhZGQgYSBjb2x1bW4gZm9yIGlmIGl0IGlzIGEgY2VsbCBvciBub3QKZGZfYmFyY29kZXMkY2VsbCA9IHJvd25hbWVzKGRmX2JhcmNvZGVzKSAlaW4lIHdoaWNoKGRmX2JhcmNvZGVzJGxvZ19saWJfc2l6ZSA+IHNwbGl0KQoKIyMgY2hhbmdlIHZhbHVlIHRvIGEgbnVtZXJpYwpkZl9iYXJjb2RlcyRjZWxsIDwtIGFzLm51bWVyaWMoZGZfYmFyY29kZXMkY2VsbCkKCiMjIGNoYW5nZSB0aGUgMCB0byBhIDIgZm9yIGVhc2Ugb2YgaGFuZGxpbmcKZGZfYmFyY29kZXMkY2VsbFtkZl9iYXJjb2RlcyRjZWxsPDFdIDwtIDIKCiMjIHJlbmFtZSB0aGUgbnVtZXJpY3MgaW50byBjZWxscyBvciBiYWNrZ3JvdW5kCmRmX2JhcmNvZGVzJGNlbGxbZGZfYmFyY29kZXMkY2VsbCA9PSAxXSA8LSAiQ2VsbHMiCmRmX2JhcmNvZGVzJGNlbGxbZGZfYmFyY29kZXMkY2VsbCA9PSAyXSA8LSAiQmFja2dyb3VuZCIKCiMjIGV4dHJhY3QgdGhlIGN1dG9mZiBmb3IgY2VsbHMgZG8geW91IGNhbiBwbG90IHRoZSBsaW5lcwpib3VuZGFyeSA8LSBhcy5udW1lcmljKHN1bShkZl9iYXJjb2RlcyRjZWxsID09ICJDZWxscyIpKQpzcGxpdCA8LSAxMF5zcGxpdAoKIyMgbWFrZSB0aGUgcGxvdApiYXJjb2RlX3Bsb3QgPC0gZ2dwbG90KGRmX2JhcmNvZGVzLCBhZXMoeD1iYXJjb2RlX3JhbmssIHk9bGliX3NpemUsIGNvbG91ciA9IGNlbGwsIHRoZW1lX3NpemUgPSA0MCkpICsKICAjIyBtYWtlIGludG8gYSBkb3QgcGxvdAogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIHNoYXBlID0gMTYpICsKICAjIyBtYWtlIHRoZSBheGlzIGludG8gbG9nIGFuZCBzcGVjaWZ5IGJyZWFrcwogIHNjYWxlX3hfbG9nMTAoYnJlYWtzID0gdHJhbnNfYnJlYWtzKCJsb2cxMCIsIGZ1bmN0aW9uKHgpIDEwXngpLCBsYWJlbHMgPSB0cmFuc19mb3JtYXQoImxvZzEwIiwgbWF0aF9mb3JtYXQoMTBeLngpKSkgKwogICMjIG1ha2UgdGhlIGF4aXMgaW50byBsb2cgYW5kIHNwZWNpZnkgYnJlYWtzCiAgc2NhbGVfeV9sb2cxMChicmVha3MgPSB0cmFuc19icmVha3MoImxvZzEwIiwgZnVuY3Rpb24oeCkgMTBeeCksIGxhYmVscyA9IHRyYW5zX2Zvcm1hdCgibG9nMTAiLCBtYXRoX2Zvcm1hdCgxMF4ueCkpKSArCiAgYW5ub3RhdGlvbl9sb2d0aWNrcygpICsKICAjIyBjaGFuZ2UgY29sb3VycyBvZiBwbG90CiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCIjYmRiZGJkIiwgIiM1YmE0M2EiKSwgbGFiZWxzID0gYygiQmFja2dyb3VuZCIsICJDZWxscyIpKSArCiAgIyMgY2hhbmdlIGFlcyBvZiBsZWdlbmQKICB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIsIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yNSksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTI1KSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTI1KSkgKwogICMjIGFkZCB0aGUgbGluZXMgb24gdGhlIHBsb3QKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB5ID0gc3BsaXQsIHhlbmQgPSBib3VuZGFyeSwgeWVuZCA9IHNwbGl0KSwgY29sb3VyID0gImJsYWNrIiwgYWxwaGEgPSAwLjAxKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gYm91bmRhcnksIHkgPSAwLCB4ZW5kID0gYm91bmRhcnksIHllbmQgPSBzcGxpdCksIGNvbG91ciA9ICJibGFjayIsIGFscGhhID0gMC4wMSkgKwogICMjIGNoYW5nZSB0aGUgYXhpcyBsYWJlbHMKICBsYWJzKHggPSAiQmFyY29kZXMiLCB5ID0gIlVNSSBDb3VudHMiLCBjb2xvdXI9IkNlbGwgRGVzaWduYXRpb24iKSArCiAgIyMgY2hhbmdlIHRoZSBzaXplIG9mIHRoZSBsZWdlbmQKICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT0xMCkpKSArCiAgIyMgZml4IGF4aXMKICBjb29yZF9maXhlZCgpICsKICAjIyBtYWtlIGl0IGxvb2sgcHJldHR5CiAgdGhlbWVfbGlnaHQoKQoKIyMgcHJpbnQgdGhlIHBsb3QKYmFyY29kZV9wbG90CgojIyBzYXZlIHRoZSBwbG90Cmdnc2F2ZSgiYmFyY29kZV9wbG90XzVrLnBuZyIsIHBsb3QgPSBiYXJjb2RlX3Bsb3QsIGRldmljZSA9ICJwbmciLCBoZWlnaHQgPSAxNSwgd2lkdGggPSAxNSwgdW5pdHMgPSAiY20iLCBwYXRoID0gIi9Vc2Vycy9BbmR5L0dDU0tPL0dDU0tPX2FuYWx5c2lzX2dpdC9pbWFnZXNfdG9fZXhwb3J0LyIpCmBgYAoKc28gdGhlIG51bWJlciBvZiBjZWxscyB0aGF0IGlzIHJldGFpbmVkIGlzOgpgYGB7ciwgZWNobyA9IEZBTFNFfQp0YWJsZShkZl9iYXJjb2RlcyRjZWxsKQpgYGAKCiMjIyBGaWx0ZXIgdGhlIG9iamVjdAoKYGBge3IgZmlsdGVyIGNlbGxzIHZzIGJhY2tncm91bmR9CiMjIGV4dHJhY3QgdGhlIG5Db3VudCBhbmQgcm93IG5hbWVzIGZyb20gdGhlIFNldXJhdCBvYmplY3QKdXBiIDwtIGRhdGEuZnJhbWUobkNvdW50X1JOQSA9IHRlbng1a0BtZXRhLmRhdGEkbkNvdW50X1JOQSwgcm93Lm5hbWVzID0gcm93bmFtZXModGVueDVrQG1ldGEuZGF0YSkpCgojIyBtYWtlIGJsYW5rIGNvbHVtbiBmb3IgcmFuawp1cGIkcmFuayA8LSBOQQoKIyMgb3JkZXIgYnkgbkNvdW50cwpvcmRlci5zY29yZXMgPC0gb3JkZXIodXBiJG5Db3VudF9STkEsIGRlY3JlYXNpbmcgPSBUUlVFKQoKIyMgYWRkIGEgcmFuayB0byB0aGlzIGNvbHVtbiBiYXNlZCBvbiB0aGUgb3JkZXJlZCBuQ291bnQKdXBiJHJhbmtbb3JkZXIuc2NvcmVzXSA8LSAxOm5yb3codXBiKQoKIyMgaW5zcGVjdAojaGVhZCh1cGIpCgojIyBtYWtlIGEgbGlzdCBvZiBjZWxscyB0byByZXRhaW4gaW4gdGhlIFNldXJhdCBvYmplY3QKa2VlcF9jZWxscyA8LSByb3duYW1lcyh1cGJbd2hpY2godXBiJHJhbmsgPCA3NzYzKSxdKQoKIyMgc3Vic2V0IFNldXJhdCBvYmplY3QgdG8gaW5jbHVkZSBjZWxscyBhbmQgZGlzY2FyZCBiYWNrZ3JvdW5kCnBiX3NleCA8LSBzdWJzZXQodGVueDVrLCBjZWxscyA9IGtlZXBfY2VsbHMpCmBgYAoKIyA0LiBGaWx0ZXIgT3V0IFBvb3ItUXVhbGl0eSBDZWxscyB7LnRhYnNldH0KCiMjIyBGaWx0ZXIgTWl0b2Nob25kcmlhbCAlCgpNaXRvY2hvbmRyaWFsIGNlbGwgY291bnRzCmBgYHtyLCBmaWcuaGVpZ2h0ID0gMTUsIGZpZy53aWR0aCA9IDd9CiMjIGV4dHJhY3QgbWl0b2Nob25kcmlhbCBnZW5lcyAKI21pdG9fZ2VuZXMgPC0gZ3RmW3doaWNoKGd0ZiRWMyA9PSAiclJOQSIpLF0kVjkKI21pdG9fZ2VuZXMgPC0gZ3N1YigiOy4qIiwiIiwgZ3N1YigiZ2VuZV9pZCAiLCAiIiwgbWl0b19nZW5lcykpCiNwYXN0ZSgiVGhlc2UgYXJlIHRoZSBtaXRvY2hvbmRyaWFsIGdlbmVzIikKI2hlYWQobWl0b19nZW5lcykKCiMjIGV4dHJhY3QgbWl0byBnZW5lcwptaXRvX2dlbmVzIDwtIHBiX3NleEBhc3NheXMkUk5BQGNvdW50c0BEaW1uYW1lc1tbMV1dW2dyZXAoIl5QQkFOS0EtTUlUIixwYl9zZXhAYXNzYXlzJFJOQUBjb3VudHNARGltbmFtZXNbWzFdXSldCgojIyBwbG90IHRoZSBnZW5lcyBpbmRpdmlkdWFsbHkKVmxuUGxvdChvYmplY3QgPSBwYl9zZXgsIGZlYXR1cmVzID0gbWl0b19nZW5lcywgcHQuc2l6ZSA9IDAuMDEpCgojIyBtYWtlIGEgcGVyY2VudGFnZSBtaXRvY29uZHJpYWwgZm9yIGVhY2ggY2VsbCAodGhpcyB3aWxsIHdvcmsgYXMgbG9uZyBhcyB5b3UgZmlsdGVyIGNlbGxzIG91dCB3aXRoIHplcm8gY291bnRzKQpwYl9zZXggPC0gUGVyY2VudGFnZUZlYXR1cmVTZXQocGJfc2V4LCBwYXR0ZXJuID0gIl5QQkFOS0EtTUlUIiwgY29sLm5hbWUgPSAicGVyY2VudC5tdCIpCmBgYAoKcGxvdCBwZXJjZW50YWdlIG1pdG9jaG9uZHJpYWwKYGBge3J9CiMjIHBsb3QgZm9yIHBlcmNlbnRhZ2Ugb2YgbWl0b2Nob25kcmlhbCByZWFkcwp2MSA8LSBWbG5QbG90KG9iamVjdCA9IHBiX3NleCwgZmVhdHVyZXMgPSAicGVyY2VudC5tdCIsIHB0LnNpemUgPSAwLjAxKSArCiAgIyMgYWRkIGEgbGluZSB3aGVyZSB3ZSB3aWxsIGZpbHRlcgogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDIwLCBjb2w9ImJsdWUiKSArCiAgIyMgY2hhbmdlIGxhYmVscwogIGxhYnMoeCA9ICIiLHkgPSAiJSBNaXRvY2hvbmRyaWFsIFJlYWRzIiwgdGl0bGUgPSAiTWl0b2Nob25kcmlhbCBwZXIgY2VsbCIpICsKICAjIyByZW1vdmUgbGVnZW5kCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgIyMgY2hhbmdlIGFwcGVhcmFuY2UKICB0aGVtZV9jbGFzc2ljKCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz0iZ3JleSIpICsKICAjc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMTAwKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzLng9ZWxlbWVudF9ibGFuaygpLCB0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjApLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0yMCksIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0yMCksIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChjb2xvdXI9ImJsYWNrIikpCgojIyBwcmludAp2MQoKIyMgc2F2ZQojZ2dzYXZlKCJ+L2ltYWdlc190b19leHBvcnQvUUNfMTBYX21pdG9fdmlvbGluLnBuZyIsIHBsb3QgPSBRQ19taXRvX3Zpb2xpbiwgZGV2aWNlID0gInBuZyIsIHBhdGggPSBOVUxMLCBzY2FsZSA9IDEsIHdpZHRoID0gMTUsIGhlaWdodCA9IDEwLCB1bml0cyA9ICJjbSIsIGRwaSA9IDMwMCwgbGltaXRzaXplID0gVFJVRSkKYGBgCgpJbiB0aGUgU21hcnQtc2VxMiBkYXRhLCB3ZSB1c2UgYSB0aHJlc2hvbGQgb2YgMjAlLiBObyBjZWxsIGluIG91ciAxMFggZGF0YSBpcyBoaWdoZXIgdGhhbiAxMyUgYW5kIG9ubHkKYGBge3IsIGVjaG8gPSBGQUxTRX0Kc3VtKHBiX3NleEBtZXRhLmRhdGEkcGVyY2VudC5tdCA+IDUpCmBgYApjZWxscyBoYXZlIGEgJSBtaXRvY2hvbmRyaWFsIHJlYWRzIGFib3ZlIDUlLiAKCiMjIyBuR2VuZXMgZmlsdGVyCgpwbG90IGluZGl2aWR1YWwgdmlvbGluIHBsb3RzCmBgYHtyfQojIyBuR2VuZXMgcGxvdApnZW5lX3Bsb3RfNWsgPC0gVmxuUGxvdChvYmplY3QgPSBwYl9zZXgsIGZlYXR1cmVzID0gIm5GZWF0dXJlX1JOQSIsIHB0LnNpemUgPSAwLjAxKQoKIyMgaW1wcm92ZSB0aGUgYWVzdGhldGljcwpnZW5lX3Bsb3RfNWsgPC0gZ2VuZV9wbG90XzVrICsgCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMjAwLCBjb2w9ImJsdWUiKSArCiAgbGFicyh4ID0gIiIseSA9ICJuR2VuZSIsIHRpdGxlID0gIkdlbmVzIHBlciBjZWxsIikgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPSJncmV5IikgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDMwMDApKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCksIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTIwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTIwKSwgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KGNvbG91cj0iYmxhY2siKSkKCiMjIG5VTUkgcGxvdApudW1pX3Bsb3RfNWsgPC0gVmxuUGxvdChvYmplY3QgPSBwYl9zZXgsIGZlYXR1cmVzID0gIm5Db3VudF9STkEiLCBwdC5zaXplID0gMC4wMSkKCiMjIGltcHJvdmUgYWVzdGhldGljcwpudW1pX3Bsb3RfNWsgPC0gbnVtaV9wbG90XzVrICsKICBsYWJzKHggPSAiIix5ID0gIm5VTUkiLCB0aXRsZSA9ICJVTUlzIHBlciBjZWxsIikgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPSJncmV5IikgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDMwMDApKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCksIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTIwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTIwKSwgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KGNvbG91cj0iYmxhY2siKSkKCiMjIHBsb3QgdG9nZXRoZXIKZ3JpZC5hcnJhbmdlKGdlbmVfcGxvdF81aywgbnVtaV9wbG90XzVrLCBuY29sID0gMiwgdG9wPXRleHRHcm9iKCI1SyBjZWxscyAxMFgiLCBncD1ncGFyKGZvbnRzaXplPTE1LGZvbnQ9OCkpKQoKIyMgc2F2ZSBuR2VuZSBwbG90IG9uIGl0cyBvd24KI2dnc2F2ZSgibmdlbmVfcGxvdC5wZGYiLCBwbG90ID0gZ2VuZTEsIGRldmljZSA9ICJwZGYiLCBoZWlnaHQgPSA1LCB3aWR0aCA9IDUsIHVuaXRzID0gImluIiwgcGF0aCA9ICIvVXNlcnMvYXIxOS9EZXNrdG9wL1BoRC9HQ1NLT19BbmFseXNpcyIpCmBgYAoKcGxvdCB0d28gbWV0cmljcyB0b2dldGhlcgpgYGB7ciwgZmlnLmhlaWdodCA9IDgsIGZpZy53aWR0aCA9IDh9CiMjIG1ha2UgYSBkYXRhZnJhbWUgZm9yIGltcG9ydGFudCBmaWx0ZXJpbmcgbWV0cmljcwpkZiA8LSBkYXRhLmZyYW1lKG5Db3VudCA9IGxvZzEwKHBiX3NleEBtZXRhLmRhdGEkbkNvdW50X1JOQSksIG5HZW5lcyA9IHBiX3NleEBtZXRhLmRhdGEkbkZlYXR1cmVfUk5BLCBwZXJjZW50X210ID0gcGJfc2V4QG1ldGEuZGF0YSRwZXJjZW50Lm10LCBleHBlcmltZW50ID0gcGJfc2V4QG1ldGEuZGF0YSRleHBlcmltZW50KQoKIyMgcGxvdCBtYWluIGRvdHBsb3QKcGxvdDEgPC0gZ2dwbG90KGRmLCBhZXMoeCA9IG5Db3VudCwgeSA9IG5HZW5lcykpICsgCiAgZ2VvbV9wb2ludChhZXMoKSwgc2l6ZSA9IDAuMSkgKwogIGdlb21fcnVnKCkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZSA9ICJOdW1iZXIgb2YgRGV0ZWN0ZWQgR2VuZXMiKSArIAogIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gImxvZzEwKE51bWJlciBvZiBUb3RhbCBVTUkpIikgKyAKICB0aGVtZV9wdWJyKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTIwMCkKCiMjIHBsb3QgZGVuc2l0eSBwbG90IHgKZGVuczEgPC0gZ2dwbG90KGRmLCBhZXMoeCA9IG5Db3VudCkpICsgCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC4yKSArIAogIHRoZW1lX3ZvaWQoKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCiMjIHBsb3QgZGVuc2l0eSBwbG90IHkKIyMgb2xkIG1ldGhvZAojIGRlbnMyIDwtIGdncGxvdChkZiwgYWVzKHggPSBuR2VuZXMsIHkgPSBleHBlcmltZW50KSkgKwojICAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC4yKSArCiMgICB0aGVtZV92b2lkKCkgKwojICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiMgICBjb29yZF9mbGlwKCkKIyMgbmV3IG1ldGhvZApkZW5zIDwtIGRlbnNpdHkoZGYkbkdlbmVzKQpkdCA8LSBkYXRhLmZyYW1lKHg9ZGVucyR4LCB5PWRlbnMkeSkKcHJvYnMgPC0gYygwLCAwLjI1LCAwLjUsIDAuNzUsIDEpCnF1YW50aWxlcyA8LSBxdWFudGlsZShkZiRuR2VuZXMsIHByb2I9cHJvYnMpCmR0JHF1YW50IDwtIGZhY3RvcihmaW5kSW50ZXJ2YWwoZHQkeCxxdWFudGlsZXMpKQpkZW5zMiA8LSBnZ3Bsb3QoZHQsIGFlcyh4LHkpKSArIGdlb21fbGluZSgpICsgZ2VvbV9yaWJib24oYWVzKHltaW49MCwgeW1heD15LCBmaWxsPXF1YW50KSkgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXF1YW50aWxlcykgKyBzY2FsZV9maWxsX2JyZXdlcihndWlkZT0ibm9uZSIpICsgdGhlbWVfdm9pZCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgY29vcmRfZmxpcCgpCgojIyBwbG90IHRvZ2V0aGVyClFDX2NvbXBvc2l0ZV9wbG90IDwtIGRlbnMxICsgcGxvdF9zcGFjZXIoKSArIHBsb3QxICsgZGVuczIgKyBwbG90X2xheW91dChuY29sID0gMiwgbnJvdyA9IDIsIHdpZHRocyA9IGMoNCwgMSksIGhlaWdodHMgPSBjKDEsIDQpKQoKIyMgcHJpbnQKUUNfY29tcG9zaXRlX3Bsb3QKCiMjIHNhdmUKZ2dzYXZlKCIvVXNlcnMvQW5keS9HQ1NLTy9HQ1NLT19hbmFseXNpc19naXQvaW1hZ2VzX3RvX2V4cG9ydC9RQ18xMFhfY29tcG9zaXRlX3Bsb3QucG5nIiwgcGxvdCA9IFFDX2NvbXBvc2l0ZV9wbG90LCBkZXZpY2UgPSAicG5nIiwgcGF0aCA9IE5VTEwsIHNjYWxlID0gMSwgd2lkdGggPSAyMCwgaGVpZ2h0ID0gMjAsIHVuaXRzID0gImNtIiwgZHBpID0gMzAwLCBsaW1pdHNpemUgPSBUUlVFKQpgYGAKCiMjIyBGaWx0ZXJpbmcKClRoZSB0aHJlc2hvbGQgdXNlZCBpbiB0aGUgbWFsYXJpYSBjZWxsIGF0bGFzIHdhcyAyMzAgZm9yIFBiIGJ1dCB0aGlzIGlzIGRlcGVuZGVudCBvbiBzZXF1ZW5jaW5nIGRlcHRoIGV0Yy4KV2UgY2FuIHBsb3QgdGhlIG51bWJlciBvZiBjZWxscyByZWNvdmVyZWQgZm9yIGEgcmFuZ2Ugb2YgdGhyZXNob2xkczoKYGBge3J9CnBhc3RlKCJvcmlnaW5hbCBudW1iZXIgb2YgY2VsbHMgPSIsIG5yb3cocGJfc2V4QG1ldGEuZGF0YSkpCnBhc3RlKCJ3aXRoID4xNTAgZmlsdGVyID0iLCBucm93KHBiX3NleEBtZXRhLmRhdGFbcGJfc2V4QG1ldGEuZGF0YSRuRmVhdHVyZV9STkEgPiAxNTAsIF0pKQpwYXN0ZSgid2l0aCA+MjAwIGZpbHRlciA9IiwgbnJvdyhwYl9zZXhAbWV0YS5kYXRhW3BiX3NleEBtZXRhLmRhdGEkbkZlYXR1cmVfUk5BID4gMjAwLCBdKSkKcGFzdGUoIndpdGggPjIzMCBmaWx0ZXIgPSIsIG5yb3cocGJfc2V4QG1ldGEuZGF0YVtwYl9zZXhAbWV0YS5kYXRhJG5GZWF0dXJlX1JOQSA+IDIzMCwgXSkpCmBgYAoKU2luY2Ugd2UgaGF2ZSBhbHJlYWR5IGZpbHRlcmVkIG9uIG5VTUksIHdlIHdpbGwgZmlsdGVyIHdpdGggMjAwLgoKYGBge3J9CiMjIG51bWJlciBvZiBjZWxscyBiZWZvcmUgZmlsdGVyaW5nCnBiX3NleF9wcmVfZmlsdGVyX25DZWxscyA8LSBucm93KHBiX3NleEBtZXRhLmRhdGEpCiMjIGZpbHRlciBvYmplY3QKcGJfc2V4IDwtIHN1YnNldChwYl9zZXgsIHN1YnNldCA9IG5GZWF0dXJlX1JOQSA+IDIwMCkKIyMgbnVtYmVyIG9mIGNlbGxzIGFmdGVyIGZpbHRlcmluZwpwYl9zZXhfcG9zdF9maWx0ZXJfbkNlbGxzIDwtIG5yb3cocGJfc2V4QG1ldGEuZGF0YSkKIyMgcHJpbnQgcmVzdWx0cyBvZiBmaWx0ZXJpbmcKcGFzdGUoIm51bWJlciBvZiBjZWxscyBwcmUtZmlsdGVyIiwgcGJfc2V4X3ByZV9maWx0ZXJfbkNlbGxzKQpwYXN0ZSgibnVtYmVyIG9mIGNlbGxzIHBvc3QtZmlsdGVyIiwgcGJfc2V4X3Bvc3RfZmlsdGVyX25DZWxscykKcGFzdGUoKHBiX3NleF9wcmVfZmlsdGVyX25DZWxscyAtIHBiX3NleF9wb3N0X2ZpbHRlcl9uQ2VsbHMpLCAiY2VsbHMgd2VyZSByZW1vdmVkIGJ5IGZpbHRlcmluZyBvbiBudW1iZXIgb2YgZ2VuZXMuIikKYGBgCgojIDUuIERpbWVuc2lvbmFsaXR5IFJlZHVjdGlvbiBhbmQgQ2x1c3RlcmluZyB7LnRhYnNldH0KCiMjIyBQcmVwYXJlIGRhdGEKYGBge3J9CiMjIG5vcm1hbGlzZSBvYmplY3QKcGJfc2V4IDwtIE5vcm1hbGl6ZURhdGEocGJfc2V4LCBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJMb2dOb3JtYWxpemUiLCBzY2FsZS5mYWN0b3IgPSAxMDAwMCkKCiMjIGZpbmQgdmFyaWFibGUgZ2VuZXMKcGJfc2V4IDwtIEZpbmRWYXJpYWJsZUZlYXR1cmVzKHBiX3NleCwgc2VsZWN0aW9uLm1ldGhvZCA9ICJ2c3QiLCBuZmVhdHVyZXMgPSAyMDAwKQoKIyMgc2NhbGUgdGhlIGRhdGEKYWxsLmdlbmVzIDwtIHJvd25hbWVzKHBiX3NleCkKcGJfc2V4IDwtIFNjYWxlRGF0YShwYl9zZXgsIGZlYXR1cmVzID0gYWxsLmdlbmVzKQpgYGAKCiMjIyBQQ0EKYGBge3J9CiMjIHJ1biBQQ0EKcGJfc2V4IDwtIFJ1blBDQShwYl9zZXgsIGZlYXR1cmVzID0gVmFyaWFibGVGZWF0dXJlcyhvYmplY3QgPSBwYl9zZXgpKQoKIyMgcGxvdCAKRGltUGxvdChwYl9zZXgsIHJlZHVjdGlvbiA9ICJwY2EiKQoKIyMgZWxib3cgcGxvdApFbGJvd1Bsb3QocGJfc2V4LCBuZGltcyA9IDMwLCByZWR1Y3Rpb24gPSAicGNhIikKYGBgCgojIyMgVU1BUApgYGB7cn0KIyMgcnVuIFVNQVAKcGJfc2V4IDwtIFJ1blVNQVAocGJfc2V4LCBkaW1zID0gMToxMCwgc2VlZC51c2UgPSAxMjM0LCBuLm5laWdoYm9ycyA9IDE1MCkKCiMjIHBsb3QKRGltUGxvdChwYl9zZXgsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiaWRlbnQiLCBsYWJlbCA9IFRSVUUpCgojIyBUaGVzZSBhcmUgdGhlIHBhcmFtZXRlcnMgdXNlZCBpbiB0aGUgbWVyZ2UgVU1BUAojcGJfc2V4IDwtIFJ1blVNQVAocGJfc2V4LCByZWR1Y3Rpb24gPSAicGNhIiwgZGltcyA9IDE6MTAsIG4ubmVpZ2hib3JzID0gMTUwLCBzZWVkLnVzZSA9IDEyMzQsIG1pbi5kaXN0ID0gMC40LCByZXB1bHNpb24uc3RyZW5ndGggPSAwLjAzLCBsb2NhbC5jb25uZWN0aXZpdHkgPSAxNTApCiNEaW1QbG90KHBiX3NleCwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJpZGVudCIsIGxhYmVsID0gVFJVRSkKYGBgCgpjb2xvdXIgd2l0aCBhIGZldyBtYXJrZXIgZ2VuZXM6CmBgYHtyLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gOH0KIyBQQkFOS0EtMDUxNTAwMCAtIHAyNSAtIGZlbWFsZQojIFBCQU5LQS0xMjEyNjAwIC0gSEFQMiAtIG1hbGUKIyBQQkFOS0EtMDYwMDYwMCAtIE5FSzMgLSBtYWxlCiMgUEJBTktBLTA4MzEwMDAgLSBNU1AxIC0gbGF0ZSBhc2V4dWFsCiMgUEJBTktBLTEzMTU3MDAgLSBST04yIC0gKGFzZXh1YWxzIGFuZCBzb21lIG1hbGU/KQojIFBCQU5LQS0wNDE2MTAwIC0gZHluZW5pbiBoZWF2eSBjaGFpbiAtIG1hbGUgLSB1c2VkIGluIDgyMCBsaW5lCiMgUEJBTktBLTEzMTk1MDAgLSBDQ1AyIC0gZmVtYWxlIC0gdXNlZCBpbiA4MjAgbGluZSAKIyBQQkFOS0EtMTQzNzUwMCAtIEFQMi1HIC0gc2V1eGFsIGNvbW1pdG1lbnQgZ2VuZQojIFBCQU5LQS0xMTAyMjAwIC0gTVNQOCAtIGVhcmx5IGFzZXh1YWwgKGZyb20gQm96ZGVjaCBwYXBlcikKCkZlYXR1cmVQbG90KHBiX3NleCwgZmVhdHVyZXMgPSBjKCJQQkFOS0EtMDUxNTAwMCIsICJQQkFOS0EtMTIxMjYwMCIsIlBCQU5LQS0wNjAwNjAwIiwgIlBCQU5LQS0wODMxMDAwIiwgIlBCQU5LQS0xMzE1NzAwIiwgIlBCQU5LQS0wNDE2MTAwIiwgIlBCQU5LQS0xMzE5NTAwIiwgIlBCQU5LQS0xNDM3NTAwIiwgIlBCQU5LQS0xMTAyMjAwIikpCmBgYAoKIyMjIENsdXN0ZXJpbmcKYGBge3J9CnBiX3NleCA8LSBGaW5kTmVpZ2hib3JzKHBiX3NleCwgZGltcyA9IDE6MjEpCnBiX3NleCA8LSBGaW5kQ2x1c3RlcnMocGJfc2V4LCByZXNvbHV0aW9uID0gMSkKYGBgCgojIDYuIFJlbW92ZSBEb3VibGV0cyB7LnRhYnNldH0KCiMjIyBEb3VibGV0RmluZGVyCgpEb3VibGV0RmluZGVyIGZ1bmN0aW9uCmBgYHtyfQojIyBEb3VibGV0RmluZGVyIHNob3VsZCBiZSBhYmxlIHRvIGJlIGluc3RhbGxlZCBhbmQgcnVuIGFzIHNvOgojZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCdjaHJpcy1tY2dpbm5pcy11Y3NmL0RvdWJsZXRGaW5kZXInKQojbGlicmFyeShkb3VibGV0RmluZGVyKSAjYWxsb3dzIHJlbW92YWwgb2YgZG91YmxldHMKIyMgYnV0IHRoZXJlIHNlZW1zIHRvIGJlIHNvbWUgcHJvYmxlbXMgd2l0aCB0aGlzIHNvIHdlIHdpbGwgcnVuIGl0IGZyb20gdGhlIGdpdGh1YiBjb2RlCgojIyB0aGUgZG91YmxldCBmaW5kZXIgZnVuY3Rpb24KZG91YmxldEZpbmRlcl92MyA8LSBmdW5jdGlvbihzZXUsIFBDcywgcE4gPSAwLjI1LCBwSywgbkV4cCwgcmV1c2UucEFOTiA9IEZBTFNFLCBzY3QgPSBGQUxTRSkgewogIHJlcXVpcmUoU2V1cmF0KTsgcmVxdWlyZShmaWVsZHMpOyByZXF1aXJlKEtlcm5TbW9vdGgpCgogICMjIEdlbmVyYXRlIG5ldyBsaXN0IG9mIGRvdWJsZXQgY2xhc3NpZmljYXRvbnMgZnJvbSBleGlzdGluZyBwQU5OIHZlY3RvciB0byBzYXZlIHRpbWUKICBpZiAocmV1c2UucEFOTiAhPSBGQUxTRSApIHsKICAgIHBBTk4ub2xkIDwtIHNldUBtZXRhLmRhdGFbICwgcmV1c2UucEFOTl0KICAgIGNsYXNzaWZpY2F0aW9ucyA8LSByZXAoIlNpbmdsZXQiLCBsZW5ndGgocEFOTi5vbGQpKQogICAgY2xhc3NpZmljYXRpb25zW29yZGVyKHBBTk4ub2xkLCBkZWNyZWFzaW5nPVRSVUUpWzE6bkV4cF1dIDwtICJEb3VibGV0IgogICAgc2V1QG1ldGEuZGF0YVssIHBhc3RlKCJERi5jbGFzc2lmaWNhdGlvbnMiLHBOLHBLLG5FeHAsc2VwPSJfIildIDwtIGNsYXNzaWZpY2F0aW9ucwogICAgcmV0dXJuKHNldSkKICB9CgogIGlmIChyZXVzZS5wQU5OID09IEZBTFNFKSB7CiAgICAjIyBNYWtlIG1lcmdlZCByZWFsLWFydGlmaWNhbCBkYXRhCiAgICByZWFsLmNlbGxzIDwtIHJvd25hbWVzKHNldUBtZXRhLmRhdGEpCiAgICBkYXRhIDwtIHNldUBhc3NheXMkUk5BQGNvdW50c1ssIHJlYWwuY2VsbHNdCiAgICBuX3JlYWwuY2VsbHMgPC0gbGVuZ3RoKHJlYWwuY2VsbHMpCiAgICBuX2RvdWJsZXRzIDwtIHJvdW5kKG5fcmVhbC5jZWxscy8oMSAtIHBOKSAtIG5fcmVhbC5jZWxscykKICAgIHByaW50KHBhc3RlKCJDcmVhdGluZyIsbl9kb3VibGV0cywiYXJ0aWZpY2lhbCBkb3VibGV0cy4uLiIsc2VwPSIgIikpCiAgICByZWFsLmNlbGxzMSA8LSBzYW1wbGUocmVhbC5jZWxscywgbl9kb3VibGV0cywgcmVwbGFjZSA9IFRSVUUpCiAgICByZWFsLmNlbGxzMiA8LSBzYW1wbGUocmVhbC5jZWxscywgbl9kb3VibGV0cywgcmVwbGFjZSA9IFRSVUUpCiAgICBkb3VibGV0cyA8LSAoZGF0YVssIHJlYWwuY2VsbHMxXSArIGRhdGFbLCByZWFsLmNlbGxzMl0pLzIKICAgIGNvbG5hbWVzKGRvdWJsZXRzKSA8LSBwYXN0ZSgiWCIsIDE6bl9kb3VibGV0cywgc2VwID0gIiIpCiAgICBkYXRhX3dkb3VibGV0cyA8LSBjYmluZChkYXRhLCBkb3VibGV0cykKCiAgICAjIyBTdG9yZSBpbXBvcnRhbnQgcHJlLXByb2Nlc3NpbmcgaW5mb3JtYXRpb24KICAgIG9yaWcuY29tbWFuZHMgPC0gc2V1QGNvbW1hbmRzCgogICAgIyMgUHJlLXByb2Nlc3MgU2V1cmF0IG9iamVjdAogICAgaWYgKHNjdCA9PSBGQUxTRSkgewogICAgICBwcmludCgiQ3JlYXRpbmcgU2V1cmF0IG9iamVjdC4uLiIpCiAgICAgIHNldV93ZG91YmxldHMgPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KGNvdW50cyA9IGRhdGFfd2RvdWJsZXRzKQoKICAgICAgcHJpbnQoIk5vcm1hbGl6aW5nIFNldXJhdCBvYmplY3QuLi4iKQogICAgICBzZXVfd2RvdWJsZXRzIDwtIE5vcm1hbGl6ZURhdGEoc2V1X3dkb3VibGV0cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gb3JpZy5jb21tYW5kcyROb3JtYWxpemVEYXRhLlJOQUBwYXJhbXMkbm9ybWFsaXphdGlvbi5tZXRob2QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY2FsZS5mYWN0b3IgPSBvcmlnLmNvbW1hbmRzJE5vcm1hbGl6ZURhdGEuUk5BQHBhcmFtcyRzY2FsZS5mYWN0b3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXJnaW4gPSBvcmlnLmNvbW1hbmRzJE5vcm1hbGl6ZURhdGEuUk5BQHBhcmFtcyRtYXJnaW4pCgogICAgICBwcmludCgiRmluZGluZyB2YXJpYWJsZSBnZW5lcy4uLiIpCiAgICAgIHNldV93ZG91YmxldHMgPC0gRmluZFZhcmlhYmxlRmVhdHVyZXMoc2V1X3dkb3VibGV0cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3Rpb24ubWV0aG9kID0gb3JpZy5jb21tYW5kcyRGaW5kVmFyaWFibGVGZWF0dXJlcy5STkEkc2VsZWN0aW9uLm1ldGhvZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2Vzcy5zcGFuID0gb3JpZy5jb21tYW5kcyRGaW5kVmFyaWFibGVGZWF0dXJlcy5STkEkbG9lc3Muc3BhbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGlwLm1heCA9IG9yaWcuY29tbWFuZHMkRmluZFZhcmlhYmxlRmVhdHVyZXMuUk5BJGNsaXAubWF4LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lYW4uZnVuY3Rpb24gPSBvcmlnLmNvbW1hbmRzJEZpbmRWYXJpYWJsZUZlYXR1cmVzLlJOQSRtZWFuLmZ1bmN0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3BlcnNpb24uZnVuY3Rpb24gPSBvcmlnLmNvbW1hbmRzJEZpbmRWYXJpYWJsZUZlYXR1cmVzLlJOQSRkaXNwZXJzaW9uLmZ1bmN0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bS5iaW4gPSBvcmlnLmNvbW1hbmRzJEZpbmRWYXJpYWJsZUZlYXR1cmVzLlJOQSRudW0uYmluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJpbm5pbmcubWV0aG9kID0gb3JpZy5jb21tYW5kcyRGaW5kVmFyaWFibGVGZWF0dXJlcy5STkEkYmlubmluZy5tZXRob2QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmZlYXR1cmVzID0gb3JpZy5jb21tYW5kcyRGaW5kVmFyaWFibGVGZWF0dXJlcy5STkEkbmZlYXR1cmVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lYW4uY3V0b2ZmID0gb3JpZy5jb21tYW5kcyRGaW5kVmFyaWFibGVGZWF0dXJlcy5STkEkbWVhbi5jdXRvZmYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzcGVyc2lvbi5jdXRvZmYgPSBvcmlnLmNvbW1hbmRzJEZpbmRWYXJpYWJsZUZlYXR1cmVzLlJOQSRkaXNwZXJzaW9uLmN1dG9mZikKCiAgICAgIHByaW50KCJTY2FsaW5nIGRhdGEuLi4iKQogICAgICBzZXVfd2RvdWJsZXRzIDwtIFNjYWxlRGF0YShzZXVfd2RvdWJsZXRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IG9yaWcuY29tbWFuZHMkU2NhbGVEYXRhLlJOQSRmZWF0dXJlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWwudXNlID0gb3JpZy5jb21tYW5kcyRTY2FsZURhdGEuUk5BJG1vZGVsLnVzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZG8uc2NhbGUgPSBvcmlnLmNvbW1hbmRzJFNjYWxlRGF0YS5STkEkZG8uc2NhbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvLmNlbnRlciA9IG9yaWcuY29tbWFuZHMkU2NhbGVEYXRhLlJOQSRkby5jZW50ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlLm1heCA9IG9yaWcuY29tbWFuZHMkU2NhbGVEYXRhLlJOQSRzY2FsZS5tYXgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJsb2NrLnNpemUgPSBvcmlnLmNvbW1hbmRzJFNjYWxlRGF0YS5STkEkYmxvY2suc2l6ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluLmNlbGxzLnRvLmJsb2NrID0gb3JpZy5jb21tYW5kcyRTY2FsZURhdGEuUk5BJG1pbi5jZWxscy50by5ibG9jaykKCiAgICAgIHByaW50KCJSdW5uaW5nIFBDQS4uLiIpCiAgICAgIHNldV93ZG91YmxldHMgPC0gUnVuUENBKHNldV93ZG91YmxldHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gb3JpZy5jb21tYW5kcyRTY2FsZURhdGEuUk5BJGZlYXR1cmVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBucGNzID0gbGVuZ3RoKFBDcyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldi5wY2EgPSAgb3JpZy5jb21tYW5kcyRSdW5QQ0EuUk5BJHJldi5wY2EsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdlaWdodC5ieS52YXIgPSBvcmlnLmNvbW1hbmRzJFJ1blBDQS5STkEkd2VpZ2h0LmJ5LnZhciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZT1GQUxTRSkKICAgICAgcGNhLmNvb3JkIDwtIHNldV93ZG91YmxldHNAcmVkdWN0aW9ucyRwY2FAY2VsbC5lbWJlZGRpbmdzWyAsIFBDc10KICAgICAgY2VsbC5uYW1lcyA8LSByb3duYW1lcyhzZXVfd2RvdWJsZXRzQG1ldGEuZGF0YSkKICAgICAgbkNlbGxzIDwtIGxlbmd0aChjZWxsLm5hbWVzKQogICAgICBybShzZXVfd2RvdWJsZXRzKTsgZ2MoKSAjIEZyZWUgdXAgbWVtb3J5CiAgICB9CgogICAgaWYgKHNjdCA9PSBUUlVFKSB7CiAgICAgIHJlcXVpcmUoc2N0cmFuc2Zvcm0pCiAgICAgIHByaW50KCJDcmVhdGluZyBTZXVyYXQgb2JqZWN0Li4uIikKICAgICAgc2V1X3dkb3VibGV0cyA8LSBDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzID0gZGF0YV93ZG91YmxldHMpCgogICAgICBwcmludCgiUnVubmluZyBTQ1RyYW5zZm9ybS4uLiIpCiAgICAgIHNldV93ZG91YmxldHMgPC0gU0NUcmFuc2Zvcm0oc2V1X3dkb3VibGV0cykKCiAgICAgIHByaW50KCJSdW5uaW5nIFBDQS4uLiIpCiAgICAgIHNldV93ZG91YmxldHMgPC0gUnVuUENBKHNldV93ZG91YmxldHMsIG5wY3MgPSBsZW5ndGgoUENzKSkKICAgICAgcGNhLmNvb3JkIDwtIHNldV93ZG91YmxldHNAcmVkdWN0aW9ucyRwY2FAY2VsbC5lbWJlZGRpbmdzWyAsIFBDc10KICAgICAgY2VsbC5uYW1lcyA8LSByb3duYW1lcyhzZXVfd2RvdWJsZXRzQG1ldGEuZGF0YSkKICAgICAgbkNlbGxzIDwtIGxlbmd0aChjZWxsLm5hbWVzKQogICAgICBybShzZXVfd2RvdWJsZXRzKTsgZ2MoKQogICAgfQoKICAgICMjIENvbXB1dGUgUEMgZGlzdGFuY2UgbWF0cml4CiAgICBwcmludCgiQ2FsY3VsYXRpbmcgUEMgZGlzdGFuY2UgbWF0cml4Li4uIikKICAgIGRpc3QubWF0IDwtIGZpZWxkczo6cmRpc3QocGNhLmNvb3JkKQoKICAgICMjIENvbXB1dGUgcEFOTgogICAgcHJpbnQoIkNvbXB1dGluZyBwQU5OLi4uIikKICAgIHBBTk4gPC0gYXMuZGF0YS5mcmFtZShtYXRyaXgoMEwsIG5yb3cgPSBuX3JlYWwuY2VsbHMsIG5jb2wgPSAxKSkKICAgIHJvd25hbWVzKHBBTk4pIDwtIHJlYWwuY2VsbHMKICAgIGNvbG5hbWVzKHBBTk4pIDwtICJwQU5OIgogICAgayA8LSByb3VuZChuQ2VsbHMgKiBwSykKICAgIGZvciAoaSBpbiAxOm5fcmVhbC5jZWxscykgewogICAgICBuZWlnaGJvcnMgPC0gb3JkZXIoZGlzdC5tYXRbLCBpXSkKICAgICAgbmVpZ2hib3JzIDwtIG5laWdoYm9yc1syOihrICsgMSldCiAgICAgIG5laWdoYm9yLm5hbWVzIDwtIHJvd25hbWVzKGRpc3QubWF0KVtuZWlnaGJvcnNdCiAgICAgIHBBTk4kcEFOTltpXSA8LSBsZW5ndGgod2hpY2gobmVpZ2hib3JzID4gbl9yZWFsLmNlbGxzKSkvawogICAgfQoKICAgIHByaW50KCJDbGFzc2lmeWluZyBkb3VibGV0cy4uIikKICAgIGNsYXNzaWZpY2F0aW9ucyA8LSByZXAoIlNpbmdsZXQiLG5fcmVhbC5jZWxscykKICAgIGNsYXNzaWZpY2F0aW9uc1tvcmRlcihwQU5OJHBBTk5bMTpuX3JlYWwuY2VsbHNdLCBkZWNyZWFzaW5nPVRSVUUpWzE6bkV4cF1dIDwtICJEb3VibGV0IgogICAgc2V1QG1ldGEuZGF0YVssIHBhc3RlKCJwQU5OIixwTixwSyxuRXhwLHNlcD0iXyIpXSA8LSBwQU5OW3Jvd25hbWVzKHNldUBtZXRhLmRhdGEpLCAxXQogICAgc2V1QG1ldGEuZGF0YVssIHBhc3RlKCJERi5jbGFzc2lmaWNhdGlvbnMiLHBOLHBLLG5FeHAsc2VwPSJfIildIDwtIGNsYXNzaWZpY2F0aW9ucwogICAgcmV0dXJuKHNldSkKICB9Cn0KIyMgdXNhZ2U6IGh0dHBzOi8vcmRyci5pby9naXRodWIvY2hyaXMtbWNnaW5uaXMtdWNzZi9Eb3VibGV0RmluZGVyL21hbi9kb3VibGV0RmluZGVyX3YzLmh0bWwKIyMgc291cmNlOiBodHRwczovL2dpdGh1Yi5jb20vY2hyaXMtbWNnaW5uaXMtdWNzZi9Eb3VibGV0RmluZGVyL2Jsb2IvbWFzdGVyL1IvZG91YmxldEZpbmRlcl92My5SCmBgYAoKUnVuIERvdWJsZXRGaW5kZXIKYGBge3J9CiMgdGhlIHR1dG9yaWFsIHJlY29tbWVuZHMgdXNpbmcgdGhpcyBhcyBhbiBhcHByb3hpbWF0aW9uOgojbkV4cF9wb2kgPC0gcm91bmQoMC4xNSpucm93KHBiX3NleEBtZXRhLmRhdGEpKQojYnV0IGEgbW9yZSBhcHByb3ByaWF0ZSBhcHByb3hpbWF0aW9uIGlzIHRoYXQgdGhlIGV4cGVjdGVkIG51bWJlciBvZiBkb3VibGV0cyBpcyB+MSUgcGVyIDEwMDAgY2VsbHMgc286Cm5FeHBfcG9pIDwtIHJvdW5kKCgwLjAxKihucm93KHBiX3NleEBtZXRhLmRhdGEpLzEwMDApKSpucm93KHBiX3NleEBtZXRhLmRhdGEpKQojcnVuIGRvdWJsZXQgZmluZGVyOgpwYl9zZXggPC0gZG91YmxldEZpbmRlcl92MyhwYl9zZXgsIFBDcyA9IDE6MjEsIHBOID0gMC4yNSwgcEsgPSAwLjAxLCBuRXhwID0gbkV4cF9wb2ksIHJldXNlLnBBTk4gPSBGQUxTRSwgc2N0ID0gRkFMU0UpCmBgYAoKcmVzdWx0cyBpbjoKYGBge3J9CnRhYmxlKHBiX3NleEBtZXRhLmRhdGEkREYuY2xhc3NpZmljYXRpb25zXzAuMjVfMC4wMV80NDApCmBgYAoKIyMjIFZhbGlkYXRpb24gYW5kIHZpc3VhbGlzYXRpb24gb2YgZG91YmxldHMKCnZpc3VhbGlzZSB3aGVyZSBkb3VibGV0cyBhcmU6CmBgYHtyfQpkb3VibGV0LmNlbGxzIDwtIGMocm93bmFtZXMocGJfc2V4QG1ldGEuZGF0YVtwYl9zZXhAbWV0YS5kYXRhJERGLmNsYXNzaWZpY2F0aW9uc18wLjI1XzAuMDFfNDQwID09ICJEb3VibGV0IixdKSkKZDEgPC0gRGltUGxvdChwYl9zZXgsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgY2VsbHMuaGlnaGxpZ2h0ID0gZG91YmxldC5jZWxscywgc2l6ZXMuaGlnaGxpZ2h0ID0gMikKI1RTTkVQbG90KG9iamVjdCA9IHBiX3NleCwgY2VsbHMuaGlnaGxpZ2h0ID0gZG91YmxldC5jZWxscywgZG8ucmV0dXJuID0gVFJVRSwgKQpkb3VibGV0MSA8LSBkMSArIGNvb3JkX2ZpeGVkKCkgKyB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCkpCgojIyBwbG90IGNsdXN0ZXJzCmNsdXN0ZXJfcGxvdCA8LSBEaW1QbG90KHBiX3NleCwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJpZGVudCIsIGxhYmVsID0gVFJVRSkKCmRvdWJsZXQxICsgY2x1c3Rlcl9wbG90CmBgYAoKYGBge3J9ClZsblBsb3Qob2JqZWN0ID0gcGJfc2V4LCBmZWF0dXJlcyA9ICJwQU5OXzAuMjVfMC4wMV80NDAiLCBwdC5zaXplID0gMC4xKQpgYGAKCmBgYHtyfQojIyBleHRyYWN0IG1ldGEgZGF0YSBjb2xzIG9mIGludGVyZXN0CmRmIDwtIHBiX3NleEBtZXRhLmRhdGFbLGMoIlJOQV9zbm5fcmVzLjEiLCJERi5jbGFzc2lmaWNhdGlvbnNfMC4yNV8wLjAxXzQ0MCIpXQoKIyMgbWFrZSBhIHRhYmxlIGZyb20gZG91YmxldHMKZGYgPC0gZGF0YS5mcmFtZShyYmluZCh0YWJsZShkZikpKQoKIyMgYW1tZW5kIHJvd25hbWVzCmRmJGNsdXN0ZXIgPC0gcm93bmFtZXMoZGYpCgojIyBjYWxjdWxhdGUgcGVyY2VudGFnZXMgb2YgY2VsbHMgdGhhdCBhcmUgZG91YmxldHMKZGYkcGNfZG91YmxldCA8LSAoKGRmWywxXSkvKChkZlssMV0pICsgZGZbLDJdKSkqMTAwCgojIyBpbnNwZWN0CiNrYWJsZShkZltvcmRlcihkZiRwY19kb3VibGV0KSxdKQoKIyMgcGxvdCAKZ2dwbG90KGRhdGE9ZGYsIGFlcyh4PWNsdXN0ZXIsIHk9cGNfZG91YmxldCkpICsKICBnZW9tX2NvbChmaWxsPSJzdGVlbGJsdWUiKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKIyMjIEZpbHRlciBkb3VibGV0cwoKSXQgZGVmaW5pdGVseSBzZWVtcyBsaWtlIHRoZXJlIGFyZSBzb21lIGJpYXNlcyBpbiBkb3VibGV0IGRldGVjdGlvbi4gRmV3ZXIgZG91YmxldHMgaW4gdmVyeSBlYXJseSByaW5ncyBhbmQgaW4gbWF0dXJlIHNleGVzIG1heSBiZSBkdWUgdG8gYSBzbWFsbGVyIG51bWJlciBvZiB0aGUgcG9wdWxhdGlvbiBiZWluZyB0aGVzZSBjZWxscy4gCgpJdCBtYXkgYWxzbyBiZSBiaW9sb2dpY2FsLCB0aGF0IHRoZXNlIGNlbGxzIGFyZSBsZXNzIGxpa2VseSB0byBhc3NvY2lhdGUgdG8gb25lIGFub3RoZXIgKGFsdGhvdWdoIGxlc3MgbGlrZWx5LCBhcyBkb3VibGV0cyBhcmUgYSByZXN1bHQgb2YgdGhlIHByb2JhYmlsaXR5IG9mIHR3byBjZWxscyBiZWluZyBjYXB0dXJlZCBpbnNpZGUgdGhlIHNhbWUgZHJvcGxldCB0b2dldGhlciBhdCBhIGNlcnRhaW4gbG9hZGluZyBjb25jZW50cmF0aW9uLCByYXRoZXIgdGhhbiB0d28gY2VsbHMgYWxyZWFkeSBiZWluZyB0b2dldGhlciB1cG9uIGRyb3BsZXQgY2FwdHVyZSkuCgpyZW1vdmUgZG91YmxldHM6CmBgYHtyfQojIyBtYWtlIGEgbGlzdCBvZiBzaW5nbGV0IGNlbGxzCmtlZXBfc2luZ2xldHMgPC0gcm93bmFtZXMocGJfc2V4QG1ldGEuZGF0YVtwYl9zZXhAbWV0YS5kYXRhJERGLmNsYXNzaWZpY2F0aW9uc18wLjI1XzAuMDFfNDQwID09ICJTaW5nbGV0IixdKQoKIyMgc3Vic2V0IGludG8gbmV3IHNldXJhdCBvYmplY3QKcGJfc2V4X2ZpbHRlcmVkIDwtIHN1YnNldChwYl9zZXgsIGNlbGxzID0ga2VlcF9zaW5nbGV0cywgc3Vic2V0LnJhdyA9IFRSVUUpCgojIyBjb21wYXJlIG9sZCBhbmQgbmV3IG9iamVjdHMKcGJfc2V4CnBiX3NleF9maWx0ZXJlZApgYGAKCiMgNy4gTGlmZSBDeWNsZSBTdGFnZSAoVXNpbmcgQnVsayBSTkEtU2VxIENvcnJlbGF0aW9uKSB7LnRhYnNldH0KCkFkZCBpbiBidWxrIGRhdGEgcHJlZGljdGlvbnMKCiMjIyBob28gZXQgYWwuCmBgYHtyfQojUGIgUHJlZGljdGlvbiBjb3JyZWxhdGlvbnMgd2l0aCBidWxrIGRhdGEgKGFzZXh1YWwgaG9vKTogCgojTG9hZCBpbiByZXF1aXJlZCBwYWNrYWdlOgpsaWJyYXJ5KEhtaXNjKQojQ29vZXJjZSBleHByZXNzaW9uIGRhdGEgaW50byBhIG1hdHJpeCBhbmQgbG9hZCBpbiB0aGUgcmVmZXJlbmNlIHRpbWVjb3Vyc2UgZGF0YToKeDEwIDwtIGFzLm1hdHJpeChwYl9zZXhfZmlsdGVyZWRAYXNzYXlzJFJOQUBkYXRhKQpyb3duYW1lcyh4MTApIDwtIGdzdWIoIi0iLCAiXyIsIHJvd25hbWVzKHgxMCkpCiNyZWFkIGluIGJ1bGsgZGF0YToKaG9vPC1hcy5tYXRyaXgocmVhZC50YWJsZSgiL1VzZXJzL0FuZHkvR0NTS08vR0NTS09fYW5hbHlzaXNfZ2l0L2RhdGEvUmVmZXJlbmNlL2hvb19iZXJnMi50eHQiLGhlYWRlcj1ULCByb3cubmFtZXM9MSkpCiNNYWtlIGEgYmxhbmsgZGF0YWZyYW1lIGluIHdoaWNoIHRvIGFkZCBwcmVkaWN0aW9uOgpkZiA8LSBkYXRhLmZyYW1lKG1hdHJpeChuY29sID0gNCwgbnJvdyA9IDApKQpjb2xuYW1lcyhkZikgPC0gYygiUHJlZGljdGlvbihTcGVhcm1hbikiLCJyKFNwZWFybWFuKSIsIlByZWRpY3Rpb24oUGVhcnNvbnMpIiwicihQZWFyc29ucykiKQojRG8gY29ycmVsYXRpb25zIHdpdGggYnVsayBkYXRhIHVzaW5nIGJvdGggU3BlYXJtYW4gYW5kIFBlYXJzb24gKGFuZCB0aGUgdG9wIDEwMDAgZ2VuZXMpOgpmb3IgKGkgaW4gMTpuY29sKHgxMCkpCnsKICBzaGFyZWQ8LWludGVyc2VjdChyb3cubmFtZXMoYXMubWF0cml4KGhlYWQoc29ydCh4MTBbLGldLCBkZWNyZWFzaW5nPVRSVUUpLDEwMDApKSkscm93Lm5hbWVzKGhvbykpCiAgc3RlcDA8LXJjb3JyKHgxMFtzaGFyZWQsaV0saG9vW3NoYXJlZCwxOjEyXSx0eXBlID0gInNwZWFybWFuIikKICBzdGVwMTwtYXMubWF0cml4KHQoc3RlcDAkclsyOjEzLDFdKSkKICBzdGVwMjwtcmNvcnIoeDEwW3NoYXJlZCxpXSxob29bc2hhcmVkLDE6MTJdLHR5cGUgPSAicGVhcnNvbiIpCiAgc3RlcDM8LWFzLm1hdHJpeCh0KHN0ZXAyJHJbMjoxMywxXSkpCiAgc3RlcDQ8LWNiaW5kKGNvbG5hbWVzKHN0ZXAxKVt3aGljaC5tYXgoc3RlcDEpXSxzdGVwMVt3aGljaC5tYXgoc3RlcDEpXSxjb2xuYW1lcyhzdGVwMylbd2hpY2gubWF4KHN0ZXAzKV0sc3RlcDNbd2hpY2gubWF4KHN0ZXAzKV0pCiAgY29sbmFtZXMoc3RlcDQpIDwtIGMoIlByZWRpY3Rpb24oU3BlYXJtYW4pIiwicihTcGVhcm1hbikiLCJQcmVkaWN0aW9uKFBlYXJzb25zKSIsInIoUGVhcnNvbnMpIikKICByb3duYW1lcyhzdGVwNCk8LWNvbG5hbWVzKHgxMClbaV0KICBkZjwtcmJpbmQoZGYsc3RlcDQpCn0KI1dyaXRlIG91dCBkYXRhIGludG8gYSBjc3YgZmlsZToKI3dyaXRlLmNzdihkZnJpbmdyLGZpbGU9Ii9Vc2Vycy9hcjE5L0Rlc2t0b3AvUGhEL0FSMDRfR0NTS09fcHJvamVjdC9BbGxfbXV0YW50c19GZWJfMjAxOC9wcmVkaWN0aW9ucGJjb21iaW5lZC5jc3YiKQojQ2hhbmdlIHRoZSBmb3JtYXQgb2YgdGhlIG91dHB1dCB0byBtYWtlIGl0IG1vcmUgcmVhZGFibGU6CiNnc3ViKCJQYl8iLCIiLCBkZnJpbmdyWywxXSkgLSBNYWtlIHByZWRpY3Rpb25zIGludG8gMThoci5kYXQgZm9ybWF0OgoKI3NwZWFybWFuOgpkZlssMV0gPC0gZ3N1YigiUGJfIiwiIiwgZGZbLDFdKQojUmVtb3ZlIGhyLmRhdCBmcm9tIGxpc3Q6CmRmWywxXSA8LSBnc3ViKCJoci5kYXQiLCIiLCBkZlssMV0pCiNDaGVjayAtIGRmcmluZ3JbLDFdCiNNYWtlIGludG8gYSBudW1iZXI6CmRmWywxXSA8LSBhcy5udW1lcmljKGRmWywxXSkKZGZbLDJdIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGRmWywyXSkpCgojcGVhcnNvbjoKZGZbLDNdIDwtIGdzdWIoIlBiXyIsIiIsIGRmWywzXSkKI1JlbW92ZSBoci5kYXQgZnJvbSBsaXN0OgpkZlssM10gPC0gZ3N1YigiaHIuZGF0IiwiIiwgZGZbLDNdKQojQ2hlY2sgLSBkZnJpbmdyWywxXQojTWFrZSBpbnRvIGEgbnVtYmVyOgpkZlssM10gPC0gYXMubnVtZXJpYyhkZlssM10pCmRmWyw0XSA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihkZlssNF0pKQojYWRkIHRvIDEwWCBvYmplY3Q6CnBiX3NleF9maWx0ZXJlZCA8LSBBZGRNZXRhRGF0YShwYl9zZXhfZmlsdGVyZWQsIG1ldGFkYXRhID0gZGYpCmBgYAoKIyMjIEthc2lhJ3MgZGF0YQpDYW4gYWxzbyBkbyB3aXRoIEthc2lhJ3MgdGltZWNvdXJzZSBkYXRhOgpgYGB7cn0Ka2FzPC1hcy5tYXRyaXgocmVhZC50YWJsZSgiL1VzZXJzL0FuZHkvR0NTS08vR0NTS09fYW5hbHlzaXNfZ2l0L2RhdGEvUmVmZXJlbmNlL0FQMk9FVEMudHh0IixoZWFkZXI9VCwgcm93Lm5hbWVzPTEpKQojTWFrZSBhIGJsYW5rIGRhdGFmcmFtZSBpbiB3aGljaCB0byBhZGQgcHJlZGljdGlvbjoKZGZzIDwtIGRhdGEuZnJhbWUobWF0cml4KG5jb2wgPSA0LCBucm93ID0gMCkpCmNvbG5hbWVzKGRmcykgPC0gYygiSUQiLCJQcmVkaWN0aW9uIiwiciAoUGVhcnNvbikiKQojRG8gY29ycmVsYXRpb25zIHdpdGggYnVsayBkYXRhIHVzaW5nIGJvdGggU3BlYXJtYW4gYW5kIFBlYXJzb24gKGFuZCB0aGUgdG9wIDEwMDAgZ2VuZXMpOgpmb3IgKGkgaW4gMTpuY29sKHgxMCkpCnsKICBzaGFyZWQ8LWludGVyc2VjdChyb3cubmFtZXMoYXMubWF0cml4KGhlYWQoc29ydCh4MTBbLGldLCBkZWNyZWFzaW5nPVRSVUUpLDEwMDApKSkscm93bmFtZXMoa2FzKSkKICBzdGVwMDwtcmNvcnIoeDEwW3NoYXJlZCxpXSxrYXNbc2hhcmVkLDE6MTBdLHR5cGUgPSAic3BlYXJtYW4iKQogIHN0ZXAxPC1hcy5tYXRyaXgodChzdGVwMCRyWzI6MTEsMV0pKQogIHN0ZXAyPC1yY29ycih4MTBbc2hhcmVkLGldLGthc1tzaGFyZWQsMToxMF0sdHlwZSA9ICJwZWFyc29uIikKICBzdGVwMzwtYXMubWF0cml4KHQoc3RlcDIkclsyOjExLDFdKSkKICBzdGVwNDwtY2JpbmQoY29sbmFtZXMoc3RlcDEpW3doaWNoLm1heChzdGVwMSldLHN0ZXAxW3doaWNoLm1heChzdGVwMSldLGNvbG5hbWVzKHN0ZXAzKVt3aGljaC5tYXgoc3RlcDMpXSxzdGVwM1t3aGljaC5tYXgoc3RlcDMpXSkKICBjb2xuYW1lcyhzdGVwNCkgPC0gYygiUHJlZGljdGlvbihTcGVhcm1hbikiLCJyKFNwZWFybWFuKSIsIlByZWRpY3Rpb24oUGVhcnNvbnMpIiwicihQZWFyc29ucykiKQogIHJvd25hbWVzKHN0ZXA0KTwtY29sbmFtZXMoeDEwKVtpXQogIGRmczwtcmJpbmQoZGZzLHN0ZXA0KQp9CiNXcml0ZSBvdXQgZGF0YSBpbnRvIGEgY3N2IGZpbGU6CiN3cml0ZS5jc3YoZGYsZmlsZT0iL1VzZXJzL2FyMTkvRGVza3RvcC9QaEQvQVIwNF9HQ1NLT19wcm9qZWN0L0FsbF9tdXRhbnRzX0ZlYl8yMDE4L3ByZWRpY3Rpb25rYXNpYWNvbWJpbmVkLmNzdiIpCgojQ2hhbmdlIHRoZSBmb3JtYXQgb2YgdGhlIG91dHB1dCB0byBtYWtlIGl0IG1vcmUgcmVhZGFibGU6CiNnc3ViKCJQYl8iLCIiLCBkZnNbLDFdKSAtIE1ha2UgcHJlZGljdGlvbnMgaW50byAxOGhyLmRhdCBmb3JtYXQ6CmRmc1ssMV0gPC0gZ3N1YigiWCIsIiIsIGRmc1ssMV0pCiNNYWtlIGludG8gYSBudW1iZXI6CmRmc1ssMV0gPC0gYXMubnVtZXJpYyhkZnNbLDFdKQojTWFrZSBpbnRvIGEgbnVtYmVyOgpkZnNbLDJdIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGRmc1ssMl0pKQoKI2dzdWIoIlBiXyIsIiIsIGRmc1ssMV0pIC0gTWFrZSBwcmVkaWN0aW9ucyBpbnRvIDE4aHIuZGF0IGZvcm1hdDoKZGZzWywzXSA8LSBnc3ViKCJYIiwiIiwgZGZzWywzXSkKI01ha2UgaW50byBhIG51bWJlcjoKZGZzWywzXSA8LSBhcy5udW1lcmljKGRmc1ssM10pCiNkZnNbLDFdCiNNYWtlIGludG8gYSBudW1iZXI6CmRmc1ssNF0gPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZGZzWyw0XSkpCgpjb2xuYW1lcyhkZnMpIDwtIGMoJ1ByZWRpY3Rpb24oU3BlYXJtYW4pX0thc2lhJywgJ3IoU3BlYXJtYW4pX0thc2lhJywgJ1ByZWRpY3Rpb24oUGVhcnNvbilfS2FzaWEnLCAncihQZWFyc29uKV9LYXNpYScpCiNhZGQgdG8gU2V1cmF0OgojYWRkIHRvIDEwWCBvYmplY3Q6CnBiX3NleF9maWx0ZXJlZCA8LSBBZGRNZXRhRGF0YShwYl9zZXhfZmlsdGVyZWQsIGRmcykKYGBgCgojIyMgVmlzdWFsaXNlCgpDb25maXJtIGxpZmUgY3ljbGUgZGVzaWduYXRpb25zOgpgYGB7cn0KIyMgcGxvdApGZWF0dXJlUGxvdChwYl9zZXhfZmlsdGVyZWQsIGZlYXR1cmVzID0gYygiUHJlZGljdGlvbi5TcGVhcm1hbi5fS2FzaWEiLCAiUHJlZGljdGlvbi5TcGVhcm1hbi4iKSkKYGBgCgpvcHRpbXNlIFVNQVAKYGBge3J9CiMjIFBDQSBjYWxjCnBiX3NleF9maWx0ZXJlZCA8LSBSdW5QQ0EocGJfc2V4X2ZpbHRlcmVkLCBmZWF0dXJlcyA9IFZhcmlhYmxlRmVhdHVyZXMob2JqZWN0ID0gcGJfc2V4X2ZpbHRlcmVkKSkKCiMjIGVsYm93IHBsb3QKRWxib3dQbG90KHBiX3NleF9maWx0ZXJlZCwgbmRpbXMgPSAzMCwgcmVkdWN0aW9uID0gInBjYSIpCgojIyBVTUFQIGNhbGMKcGJfc2V4X2ZpbHRlcmVkIDwtIFJ1blVNQVAocGJfc2V4X2ZpbHRlcmVkLCBkaW1zID0gMTo4LCBzZWVkLnVzZSA9IDMwMCwgbi5uZWlnaGJvcnMgPSA2MCwgbWluLmRpc3QgPSAwLjUsIHJlcHVsc2lvbi5zdHJlbmd0aCA9IDAuMDUsIGxvY2FsLmNvbm5lY3Rpdml0eSA9IDIwKQoKIyMgVU1BUCBwbG90CkRpbVBsb3QocGJfc2V4X2ZpbHRlcmVkLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gImlkZW50IiwgbGFiZWwgPSBUUlVFKQpgYGAKCgojIDguIFNhdmUgYW5kIEV4cG9ydCB7LnRhYnNldH0KClNhdmUgZW52aXJvbm1lbnQKYGBge3J9CiMjIFRoaXMgc2F2ZXMgZXZlcnl0aGluZyBpbiB0aGUgZ2xvYmFsIGVudmlyb25tZW50IGZvciBlYXN5IHJlY2FsbCBsYXRlcgojc2F2ZS5pbWFnZShmaWxlID0gIkdDU0tPXzEwWF9RQy5SRGF0YSIpCiNsb2FkKGZpbGUgPSAiR0NTS09fMTBYX1FDLlJEYXRhIikKYGBgCgpTYXZlIG9iamVjdChzKQpgYGB7cn0KIyMgc2F2ZSBSZGF0YQojc2F2ZShwYl8zMGtfc2V4X2ZpbHRlcmVkLCBwYl9zZXhfZmlsdGVyZWQsIGZpbGUgPSAiUGFydF8yX2lucHV0LlJkYXRhIikKI2xvYWQoZmlsZSA9ICJQYXJ0XzJfaW5wdXQuUmRhdGEiKQoKIyMgc2F2ZSBSRFMKc2F2ZVJEUyhwYl9zZXhfZmlsdGVyZWQsIGZpbGUgPSAiL1VzZXJzL0FuZHkvR0NTS08vR0NTS09fYW5hbHlzaXNfZ2l0L2RhdGFfdG9fZXhwb3J0L3BiX3NleF9maWx0ZXJlZC5SRFMiLCBjb21wcmVzcyA9IEZBTFNFKSAKI3BiX3NleF9maWx0ZXJlZCA8LSByZWFkUkRTKCJwYl9zZXhfZmlsdGVyZWQuUkRTIikKCiMjIFNhdmUgUm9iagojc2F2ZShwYl9zZXhfZmlsdGVyZWQsZmlsZT0icGJfc2V4X2ZpbHRlcmVkLlJvYmoiKQpgYGAKCiMgQXBwZW5kaXggey50YWJzZXR9CgojIyBTZXNzaW9uIEluZm8gCmBgYHtyLCBlY2hvID0gRkFMU0V9CnNlc3Npb25JbmZvKCkKYGBgCgo=